@happyvertical/smrt-core 0.30.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/AGENTS.md +124 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +265 -0
- package/bin/smrt-prebuild.js +26 -0
- package/dist/__tests__/fixtures/advisor-test-classes.d.ts +28 -0
- package/dist/__tests__/fixtures/advisor-test-classes.d.ts.map +1 -0
- package/dist/__tests__/fixtures/collection-coverage-fixtures.d.ts +12 -0
- package/dist/__tests__/fixtures/collection-coverage-fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures/inheritance-resolver-fixtures.d.ts +28 -0
- package/dist/__tests__/fixtures/inheritance-resolver-fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures/inheritance-test-classes.d.ts +43 -0
- package/dist/__tests__/fixtures/inheritance-test-classes.d.ts.map +1 -0
- package/dist/__tests__/fixtures/mcp-integration-test-classes.d.ts +18 -0
- package/dist/__tests__/fixtures/mcp-integration-test-classes.d.ts.map +1 -0
- package/dist/__tests__/fixtures/object-ai-memory-fixtures.d.ts +15 -0
- package/dist/__tests__/fixtures/object-ai-memory-fixtures.d.ts.map +1 -0
- package/dist/__tests__/fixtures/object-spec-test-classes.d.ts +13 -0
- package/dist/__tests__/fixtures/object-spec-test-classes.d.ts.map +1 -0
- package/dist/__tests__/fixtures/registry-test-classes.d.ts +23 -0
- package/dist/__tests__/fixtures/registry-test-classes.d.ts.map +1 -0
- package/dist/adapters/ai-usage.d.ts +23 -0
- package/dist/adapters/ai-usage.d.ts.map +1 -0
- package/dist/adapters/ai-usage.js +105 -0
- package/dist/adapters/ai-usage.js.map +1 -0
- package/dist/adapters/cost-rates.d.ts +20 -0
- package/dist/adapters/cost-rates.d.ts.map +1 -0
- package/dist/adapters/cost-rates.js +40 -0
- package/dist/adapters/cost-rates.js.map +1 -0
- package/dist/adapters/index.d.ts +19 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/metrics.d.ts +111 -0
- package/dist/adapters/metrics.d.ts.map +1 -0
- package/dist/adapters/metrics.js +169 -0
- package/dist/adapters/metrics.js.map +1 -0
- package/dist/adapters/pubsub.d.ts +124 -0
- package/dist/adapters/pubsub.d.ts.map +1 -0
- package/dist/adapters/pubsub.js +121 -0
- package/dist/adapters/pubsub.js.map +1 -0
- package/dist/browser.d.ts +32 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +68 -0
- package/dist/browser.js.map +1 -0
- package/dist/child-accessors.d.ts +27 -0
- package/dist/child-accessors.d.ts.map +1 -0
- package/dist/child-accessors.js +35 -0
- package/dist/child-accessors.js.map +1 -0
- package/dist/class.d.ts +313 -0
- package/dist/class.d.ts.map +1 -0
- package/dist/class.js +896 -0
- package/dist/class.js.map +1 -0
- package/dist/collection-cache.d.ts +110 -0
- package/dist/collection-cache.d.ts.map +1 -0
- package/dist/collection-cache.js +187 -0
- package/dist/collection-cache.js.map +1 -0
- package/dist/collection.d.ts +894 -0
- package/dist/collection.d.ts.map +1 -0
- package/dist/collection.js +1987 -0
- package/dist/collection.js.map +1 -0
- package/dist/config/global-config.d.ts +3 -0
- package/dist/config/global-config.d.ts.map +1 -0
- package/dist/config/global-config.js +19 -0
- package/dist/config/global-config.js.map +1 -0
- package/dist/config.d.ts +145 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/consumer-plugin/index.d.ts +22 -0
- package/dist/consumer-plugin/index.d.ts.map +1 -0
- package/dist/consumer-plugin/index.js +452 -0
- package/dist/consumer-plugin/index.js.map +1 -0
- package/dist/consumer-plugin.d.ts +8 -0
- package/dist/consumer-plugin.d.ts.map +1 -0
- package/dist/consumer-plugin.js +5 -0
- package/dist/consumer-plugin.js.map +1 -0
- package/dist/database.d.ts +95 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +32 -0
- package/dist/database.js.map +1 -0
- package/dist/decorators/compatibility.d.ts +14 -0
- package/dist/decorators/compatibility.d.ts.map +1 -0
- package/dist/decorators/compatibility.js +111 -0
- package/dist/decorators/compatibility.js.map +1 -0
- package/dist/decorators/index.d.ts +381 -0
- package/dist/decorators/index.d.ts.map +1 -0
- package/dist/decorators/index.js +104 -0
- package/dist/decorators/index.js.map +1 -0
- package/dist/dispatch/bus.d.ts +306 -0
- package/dist/dispatch/bus.d.ts.map +1 -0
- package/dist/dispatch/bus.js +583 -0
- package/dist/dispatch/bus.js.map +1 -0
- package/dist/dispatch/collections/DispatchSubscriptions.d.ts +79 -0
- package/dist/dispatch/collections/DispatchSubscriptions.d.ts.map +1 -0
- package/dist/dispatch/collections/DispatchSubscriptions.js +243 -0
- package/dist/dispatch/collections/DispatchSubscriptions.js.map +1 -0
- package/dist/dispatch/collections/Dispatches.d.ts +98 -0
- package/dist/dispatch/collections/Dispatches.d.ts.map +1 -0
- package/dist/dispatch/collections/Dispatches.js +358 -0
- package/dist/dispatch/collections/Dispatches.js.map +1 -0
- package/dist/dispatch/index.d.ts +47 -0
- package/dist/dispatch/index.d.ts.map +1 -0
- package/dist/dispatch/models/Dispatch.d.ts +101 -0
- package/dist/dispatch/models/Dispatch.d.ts.map +1 -0
- package/dist/dispatch/models/Dispatch.js +162 -0
- package/dist/dispatch/models/Dispatch.js.map +1 -0
- package/dist/dispatch/models/DispatchSubscription.d.ts +83 -0
- package/dist/dispatch/models/DispatchSubscription.d.ts.map +1 -0
- package/dist/dispatch/models/DispatchSubscription.js +112 -0
- package/dist/dispatch/models/DispatchSubscription.js.map +1 -0
- package/dist/dispatch/tenant-resolver.d.ts +98 -0
- package/dist/dispatch/tenant-resolver.d.ts.map +1 -0
- package/dist/dispatch/tenant-resolver.js +32 -0
- package/dist/dispatch/tenant-resolver.js.map +1 -0
- package/dist/dispatch/types.d.ts +149 -0
- package/dist/dispatch/types.d.ts.map +1 -0
- package/dist/embeddings/hash.d.ts +33 -0
- package/dist/embeddings/hash.d.ts.map +1 -0
- package/dist/embeddings/hash.js +37 -0
- package/dist/embeddings/hash.js.map +1 -0
- package/dist/embeddings/index.d.ts +36 -0
- package/dist/embeddings/index.d.ts.map +1 -0
- package/dist/embeddings/provider.d.ts +75 -0
- package/dist/embeddings/provider.d.ts.map +1 -0
- package/dist/embeddings/provider.js +170 -0
- package/dist/embeddings/provider.js.map +1 -0
- package/dist/embeddings/similarity.d.ts +47 -0
- package/dist/embeddings/similarity.d.ts.map +1 -0
- package/dist/embeddings/similarity.js +64 -0
- package/dist/embeddings/similarity.js.map +1 -0
- package/dist/embeddings/storage.d.ts +125 -0
- package/dist/embeddings/storage.d.ts.map +1 -0
- package/dist/embeddings/storage.js +283 -0
- package/dist/embeddings/storage.js.map +1 -0
- package/dist/embeddings/types.d.ts +250 -0
- package/dist/embeddings/types.d.ts.map +1 -0
- package/dist/errors.d.ts +363 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +669 -0
- package/dist/errors.js.map +1 -0
- package/dist/generators/cli.d.ts +162 -0
- package/dist/generators/cli.d.ts.map +1 -0
- package/dist/generators/cli.js +462 -0
- package/dist/generators/cli.js.map +1 -0
- package/dist/generators/index.d.ts +13 -0
- package/dist/generators/index.d.ts.map +1 -0
- package/dist/generators/mcp-runtime-template.d.ts +60 -0
- package/dist/generators/mcp-runtime-template.d.ts.map +1 -0
- package/dist/generators/mcp-runtime-template.js +509 -0
- package/dist/generators/mcp-runtime-template.js.map +1 -0
- package/dist/generators/mcp.d.ts +231 -0
- package/dist/generators/mcp.d.ts.map +1 -0
- package/dist/generators/mcp.js +1220 -0
- package/dist/generators/mcp.js.map +1 -0
- package/dist/generators/rest.d.ts +171 -0
- package/dist/generators/rest.d.ts.map +1 -0
- package/dist/generators/rest.js +591 -0
- package/dist/generators/rest.js.map +1 -0
- package/dist/generators/swagger.d.ts +21 -0
- package/dist/generators/swagger.d.ts.map +1 -0
- package/dist/generators/swagger.js +307 -0
- package/dist/generators/swagger.js.map +1 -0
- package/dist/generators/tenant-gate.d.ts +74 -0
- package/dist/generators/tenant-gate.d.ts.map +1 -0
- package/dist/generators/tenant-gate.js +15 -0
- package/dist/generators/tenant-gate.js.map +1 -0
- package/dist/generators.d.ts +8 -0
- package/dist/generators.d.ts.map +1 -0
- package/dist/generators.js +19 -0
- package/dist/generators.js.map +1 -0
- package/dist/hierarchical.d.ts +103 -0
- package/dist/hierarchical.d.ts.map +1 -0
- package/dist/hierarchical.js +184 -0
- package/dist/hierarchical.js.map +1 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +202 -0
- package/dist/index.js.map +1 -0
- package/dist/interceptors.d.ts +251 -0
- package/dist/interceptors.d.ts.map +1 -0
- package/dist/interceptors.js +259 -0
- package/dist/interceptors.js.map +1 -0
- package/dist/junction.d.ts +99 -0
- package/dist/junction.d.ts.map +1 -0
- package/dist/junction.js +136 -0
- package/dist/junction.js.map +1 -0
- package/dist/knowledge.d.ts +11 -0
- package/dist/knowledge.d.ts.map +1 -0
- package/dist/knowledge.js +310 -0
- package/dist/knowledge.js.map +1 -0
- package/dist/lazy-config.d.ts +160 -0
- package/dist/lazy-config.d.ts.map +1 -0
- package/dist/lazy-config.js +146 -0
- package/dist/lazy-config.js.map +1 -0
- package/dist/manifest/discover-base-classes.d.ts +78 -0
- package/dist/manifest/discover-base-classes.d.ts.map +1 -0
- package/dist/manifest/discover-base-classes.js +85 -0
- package/dist/manifest/discover-base-classes.js.map +1 -0
- package/dist/manifest/discover-smrt-packages.d.ts +48 -0
- package/dist/manifest/discover-smrt-packages.d.ts.map +1 -0
- package/dist/manifest/discover-smrt-packages.js +361 -0
- package/dist/manifest/discover-smrt-packages.js.map +1 -0
- package/dist/manifest/generator.d.ts +93 -0
- package/dist/manifest/generator.d.ts.map +1 -0
- package/dist/manifest/generator.js +380 -0
- package/dist/manifest/generator.js.map +1 -0
- package/dist/manifest/index.d.ts +16 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +51 -0
- package/dist/manifest/index.js.map +1 -0
- package/dist/manifest/manager.d.ts +51 -0
- package/dist/manifest/manager.d.ts.map +1 -0
- package/dist/manifest/manager.js +89 -0
- package/dist/manifest/manager.js.map +1 -0
- package/dist/manifest/manifest-loader.d.ts +187 -0
- package/dist/manifest/manifest-loader.d.ts.map +1 -0
- package/dist/manifest/manifest-loader.js +847 -0
- package/dist/manifest/manifest-loader.js.map +1 -0
- package/dist/manifest/sources/composite.d.ts +22 -0
- package/dist/manifest/sources/composite.d.ts.map +1 -0
- package/dist/manifest/sources/composite.js +60 -0
- package/dist/manifest/sources/composite.js.map +1 -0
- package/dist/manifest/sources/embedded.d.ts +7 -0
- package/dist/manifest/sources/embedded.d.ts.map +1 -0
- package/dist/manifest/sources/embedded.js +30 -0
- package/dist/manifest/sources/embedded.js.map +1 -0
- package/dist/manifest/sources/explicit-paths.d.ts +17 -0
- package/dist/manifest/sources/explicit-paths.d.ts.map +1 -0
- package/dist/manifest/sources/explicit-paths.js +35 -0
- package/dist/manifest/sources/explicit-paths.js.map +1 -0
- package/dist/manifest/sources/fallback.d.ts +25 -0
- package/dist/manifest/sources/fallback.d.ts.map +1 -0
- package/dist/manifest/sources/fallback.js +63 -0
- package/dist/manifest/sources/fallback.js.map +1 -0
- package/dist/manifest/sources/index.d.ts +17 -0
- package/dist/manifest/sources/index.d.ts.map +1 -0
- package/dist/manifest/sources/local-test.d.ts +7 -0
- package/dist/manifest/sources/local-test.d.ts.map +1 -0
- package/dist/manifest/sources/local-test.js +21 -0
- package/dist/manifest/sources/local-test.js.map +1 -0
- package/dist/manifest/sources/static.d.ts +7 -0
- package/dist/manifest/sources/static.d.ts.map +1 -0
- package/dist/manifest/sources/static.js +19 -0
- package/dist/manifest/sources/static.js.map +1 -0
- package/dist/manifest/sources/test.d.ts +7 -0
- package/dist/manifest/sources/test.d.ts.map +1 -0
- package/dist/manifest/sources/test.js +21 -0
- package/dist/manifest/sources/test.js.map +1 -0
- package/dist/manifest/sources/types.d.ts +79 -0
- package/dist/manifest/sources/types.d.ts.map +1 -0
- package/dist/manifest/sources/types.js +61 -0
- package/dist/manifest/sources/types.js.map +1 -0
- package/dist/manifest/static-manifest.d.ts +4 -0
- package/dist/manifest/static-manifest.d.ts.map +1 -0
- package/dist/manifest/static-manifest.js +1535 -0
- package/dist/manifest/static-manifest.js.map +1 -0
- package/dist/manifest/store.d.ts +111 -0
- package/dist/manifest/store.d.ts.map +1 -0
- package/dist/manifest/store.js +431 -0
- package/dist/manifest/store.js.map +1 -0
- package/dist/manifest/test-manifest-loader.d.ts +3 -0
- package/dist/manifest/test-manifest-loader.d.ts.map +1 -0
- package/dist/manifest/test-manifest-stub.d.ts +4 -0
- package/dist/manifest/test-manifest-stub.d.ts.map +1 -0
- package/dist/manifest/test-manifest-stub.js +80013 -0
- package/dist/manifest/test-manifest-stub.js.map +1 -0
- package/dist/manifest.d.ts +8 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +20 -0
- package/dist/manifest.js.map +1 -0
- package/dist/manifest.json +1489 -0
- package/dist/mcp-advisor/index.d.ts +499 -0
- package/dist/mcp-advisor/index.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/add-ai-methods.d.ts +6 -0
- package/dist/mcp-advisor/tools/add-ai-methods.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/configure-decorators.d.ts +6 -0
- package/dist/mcp-advisor/tools/configure-decorators.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/generate-collection.d.ts +6 -0
- package/dist/mcp-advisor/tools/generate-collection.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/generate-field-definitions.d.ts +6 -0
- package/dist/mcp-advisor/tools/generate-field-definitions.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/generate-smrt-class.d.ts +6 -0
- package/dist/mcp-advisor/tools/generate-smrt-class.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/get-object-config.d.ts +6 -0
- package/dist/mcp-advisor/tools/get-object-config.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/get-object-schema.d.ts +10 -0
- package/dist/mcp-advisor/tools/get-object-schema.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/list-registered-objects.d.ts +9 -0
- package/dist/mcp-advisor/tools/list-registered-objects.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts +9 -0
- package/dist/mcp-advisor/tools/preview-api-endpoints.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/preview-mcp-tools.d.ts +9 -0
- package/dist/mcp-advisor/tools/preview-mcp-tools.d.ts.map +1 -0
- package/dist/mcp-advisor/tools/validate-smrt-object.d.ts +6 -0
- package/dist/mcp-advisor/tools/validate-smrt-object.d.ts.map +1 -0
- package/dist/mcp-advisor/types.d.ts +209 -0
- package/dist/mcp-advisor/types.d.ts.map +1 -0
- package/dist/migrations/backfill-tracker.d.ts +84 -0
- package/dist/migrations/backfill-tracker.d.ts.map +1 -0
- package/dist/migrations/backfill-tracker.js +118 -0
- package/dist/migrations/backfill-tracker.js.map +1 -0
- package/dist/migrations/checksum.d.ts +43 -0
- package/dist/migrations/checksum.d.ts.map +1 -0
- package/dist/migrations/checksum.js +32 -0
- package/dist/migrations/checksum.js.map +1 -0
- package/dist/migrations/differ.d.ts +186 -0
- package/dist/migrations/differ.d.ts.map +1 -0
- package/dist/migrations/differ.js +601 -0
- package/dist/migrations/differ.js.map +1 -0
- package/dist/migrations/generator.d.ts +133 -0
- package/dist/migrations/generator.d.ts.map +1 -0
- package/dist/migrations/generator.js +328 -0
- package/dist/migrations/generator.js.map +1 -0
- package/dist/migrations/index.d.ts +20 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/migrations/orchestrate.d.ts +148 -0
- package/dist/migrations/orchestrate.d.ts.map +1 -0
- package/dist/migrations/orchestrate.js +118 -0
- package/dist/migrations/orchestrate.js.map +1 -0
- package/dist/migrations/tracker.d.ts +134 -0
- package/dist/migrations/tracker.d.ts.map +1 -0
- package/dist/migrations/tracker.js +624 -0
- package/dist/migrations/tracker.js.map +1 -0
- package/dist/migrations/types.d.ts +221 -0
- package/dist/migrations/types.d.ts.map +1 -0
- package/dist/migrations.d.ts +7 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +26 -0
- package/dist/migrations.js.map +1 -0
- package/dist/node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js +56 -0
- package/dist/node_modules/.pnpm/balanced-match@4.0.4/node_modules/balanced-match/dist/esm/index.js.map +1 -0
- package/dist/node_modules/.pnpm/brace-expansion@5.0.5/node_modules/brace-expansion/dist/esm/index.js +163 -0
- package/dist/node_modules/.pnpm/brace-expansion@5.0.5/node_modules/brace-expansion/dist/esm/index.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/assert-valid-pattern.js +13 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/assert-valid-pattern.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/ast.js +654 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/ast.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/brace-expressions.js +111 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/brace-expressions.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/escape.js +10 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/escape.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/index.js +824 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/index.js.map +1 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/unescape.js +10 -0
- package/dist/node_modules/.pnpm/minimatch@10.2.3/node_modules/minimatch/dist/esm/unescape.js.map +1 -0
- package/dist/object.d.ts +1202 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/object.js +2731 -0
- package/dist/object.js.map +1 -0
- package/dist/polymorphic-association.d.ts +69 -0
- package/dist/polymorphic-association.d.ts.map +1 -0
- package/dist/polymorphic-association.js +96 -0
- package/dist/polymorphic-association.js.map +1 -0
- package/dist/prebuild/cli.d.ts +7 -0
- package/dist/prebuild/cli.d.ts.map +1 -0
- package/dist/prebuild/cli.js +29 -0
- package/dist/prebuild/cli.js.map +1 -0
- package/dist/prebuild/index.d.ts +22 -0
- package/dist/prebuild/index.d.ts.map +1 -0
- package/dist/prebuild/index.js +239 -0
- package/dist/prebuild/index.js.map +1 -0
- package/dist/prebuild.d.ts +8 -0
- package/dist/prebuild.d.ts.map +1 -0
- package/dist/prebuild.js +6 -0
- package/dist/prebuild.js.map +1 -0
- package/dist/registry/cache-config.d.ts +13 -0
- package/dist/registry/cache-config.d.ts.map +1 -0
- package/dist/registry/cache-config.js +17 -0
- package/dist/registry/cache-config.js.map +1 -0
- package/dist/registry/class-registration.d.ts +31 -0
- package/dist/registry/class-registration.d.ts.map +1 -0
- package/dist/registry/class-registration.js +1074 -0
- package/dist/registry/class-registration.js.map +1 -0
- package/dist/registry/collection-resolution.d.ts +45 -0
- package/dist/registry/collection-resolution.d.ts.map +1 -0
- package/dist/registry/collection-resolution.js +121 -0
- package/dist/registry/collection-resolution.js.map +1 -0
- package/dist/registry/collision-policy.d.ts +179 -0
- package/dist/registry/collision-policy.d.ts.map +1 -0
- package/dist/registry/collision-policy.js +153 -0
- package/dist/registry/collision-policy.js.map +1 -0
- package/dist/registry/diagnostics.d.ts +58 -0
- package/dist/registry/diagnostics.d.ts.map +1 -0
- package/dist/registry/diagnostics.js +54 -0
- package/dist/registry/diagnostics.js.map +1 -0
- package/dist/registry/embedding-manager.d.ts +23 -0
- package/dist/registry/embedding-manager.d.ts.map +1 -0
- package/dist/registry/embedding-manager.js +62 -0
- package/dist/registry/embedding-manager.js.map +1 -0
- package/dist/registry/index.d.ts +13 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/inheritance-resolver.d.ts +13 -0
- package/dist/registry/inheritance-resolver.d.ts.map +1 -0
- package/dist/registry/inheritance-resolver.js +244 -0
- package/dist/registry/inheritance-resolver.js.map +1 -0
- package/dist/registry/manifest-field-merge.d.ts +4 -0
- package/dist/registry/manifest-field-merge.d.ts.map +1 -0
- package/dist/registry/manifest-field-merge.js +82 -0
- package/dist/registry/manifest-field-merge.js.map +1 -0
- package/dist/registry/name-resolver.d.ts +102 -0
- package/dist/registry/name-resolver.d.ts.map +1 -0
- package/dist/registry/name-resolver.js +241 -0
- package/dist/registry/name-resolver.js.map +1 -0
- package/dist/registry/relationship-graph.d.ts +16 -0
- package/dist/registry/relationship-graph.d.ts.map +1 -0
- package/dist/registry/relationship-graph.js +79 -0
- package/dist/registry/relationship-graph.js.map +1 -0
- package/dist/registry/schema-builder.d.ts +113 -0
- package/dist/registry/schema-builder.d.ts.map +1 -0
- package/dist/registry/schema-builder.js +474 -0
- package/dist/registry/schema-builder.js.map +1 -0
- package/dist/registry/shared-state.d.ts +62 -0
- package/dist/registry/shared-state.d.ts.map +1 -0
- package/dist/registry/shared-state.js +135 -0
- package/dist/registry/shared-state.js.map +1 -0
- package/dist/registry/types.d.ts +667 -0
- package/dist/registry/types.d.ts.map +1 -0
- package/dist/registry/validator.d.ts +13 -0
- package/dist/registry/validator.d.ts.map +1 -0
- package/dist/registry/validator.js +138 -0
- package/dist/registry/validator.js.map +1 -0
- package/dist/registry.d.ts +1358 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +2301 -0
- package/dist/registry.js.map +1 -0
- package/dist/runtime/client.d.ts +34 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +104 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/index.d.ts +10 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/mcp.d.ts +47 -0
- package/dist/runtime/mcp.d.ts.map +1 -0
- package/dist/runtime/mcp.js +72 -0
- package/dist/runtime/mcp.js.map +1 -0
- package/dist/runtime/server.d.ts +92 -0
- package/dist/runtime/server.d.ts.map +1 -0
- package/dist/runtime/server.js +390 -0
- package/dist/runtime/server.js.map +1 -0
- package/dist/runtime/types.d.ts +58 -0
- package/dist/runtime/types.d.ts.map +1 -0
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +10 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scanner/import-scanner.d.ts +7 -0
- package/dist/scanner/import-scanner.d.ts.map +1 -0
- package/dist/scanner/index.d.ts +12 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/manifest-generator.d.ts +304 -0
- package/dist/scanner/manifest-generator.d.ts.map +1 -0
- package/dist/scanner/manifest-generator.js +1707 -0
- package/dist/scanner/manifest-generator.js.map +1 -0
- package/dist/scanner/test-file-patterns.d.ts +18 -0
- package/dist/scanner/test-file-patterns.d.ts.map +1 -0
- package/dist/scanner/test-file-patterns.js +16 -0
- package/dist/scanner/test-file-patterns.js.map +1 -0
- package/dist/scanner/types.d.ts +313 -0
- package/dist/scanner/types.d.ts.map +1 -0
- package/dist/scanner/types.js +2 -0
- package/dist/scanner/types.js.map +1 -0
- package/dist/scanner.d.ts +6 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +6 -0
- package/dist/scanner.js.map +1 -0
- package/dist/schema/code-generator.d.ts +53 -0
- package/dist/schema/code-generator.d.ts.map +1 -0
- package/dist/schema/ddl/base-strategy.d.ts +80 -0
- package/dist/schema/ddl/base-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/base-strategy.js +240 -0
- package/dist/schema/ddl/base-strategy.js.map +1 -0
- package/dist/schema/ddl/duckdb-strategy.d.ts +33 -0
- package/dist/schema/ddl/duckdb-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/duckdb-strategy.js +74 -0
- package/dist/schema/ddl/duckdb-strategy.js.map +1 -0
- package/dist/schema/ddl/index.d.ts +53 -0
- package/dist/schema/ddl/index.d.ts.map +1 -0
- package/dist/schema/ddl/index.js +80 -0
- package/dist/schema/ddl/index.js.map +1 -0
- package/dist/schema/ddl/json-duckdb-strategy.d.ts +8 -0
- package/dist/schema/ddl/json-duckdb-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/json-duckdb-strategy.js +14 -0
- package/dist/schema/ddl/json-duckdb-strategy.js.map +1 -0
- package/dist/schema/ddl/postgres-strategy.d.ts +29 -0
- package/dist/schema/ddl/postgres-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/postgres-strategy.js +102 -0
- package/dist/schema/ddl/postgres-strategy.js.map +1 -0
- package/dist/schema/ddl/sqlite-strategy.d.ts +38 -0
- package/dist/schema/ddl/sqlite-strategy.d.ts.map +1 -0
- package/dist/schema/ddl/sqlite-strategy.js +74 -0
- package/dist/schema/ddl/sqlite-strategy.js.map +1 -0
- package/dist/schema/ddl/types.d.ts +114 -0
- package/dist/schema/ddl/types.d.ts.map +1 -0
- package/dist/schema/generator.d.ts +176 -0
- package/dist/schema/generator.d.ts.map +1 -0
- package/dist/schema/generator.js +1076 -0
- package/dist/schema/generator.js.map +1 -0
- package/dist/schema/index-utils.d.ts +19 -0
- package/dist/schema/index-utils.d.ts.map +1 -0
- package/dist/schema/index-utils.js +32 -0
- package/dist/schema/index-utils.js.map +1 -0
- package/dist/schema/index.d.ts +13 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/override-system.d.ts +43 -0
- package/dist/schema/override-system.d.ts.map +1 -0
- package/dist/schema/schema-aggregator.d.ts +112 -0
- package/dist/schema/schema-aggregator.d.ts.map +1 -0
- package/dist/schema/schema-manager.d.ts +95 -0
- package/dist/schema/schema-manager.d.ts.map +1 -0
- package/dist/schema/schema-manager.js +283 -0
- package/dist/schema/schema-manager.js.map +1 -0
- package/dist/schema/sql-identifiers.d.ts +107 -0
- package/dist/schema/sql-identifiers.d.ts.map +1 -0
- package/dist/schema/sql-identifiers.js +190 -0
- package/dist/schema/sql-identifiers.js.map +1 -0
- package/dist/schema/table-verifier.d.ts +10 -0
- package/dist/schema/table-verifier.d.ts.map +1 -0
- package/dist/schema/table-verifier.js +37 -0
- package/dist/schema/table-verifier.js.map +1 -0
- package/dist/schema/types.d.ts +241 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/utils.d.ts +32 -0
- package/dist/schema/utils.d.ts.map +1 -0
- package/dist/schema/utils.js +134 -0
- package/dist/schema/utils.js.map +1 -0
- package/dist/scripts/create-wrappers.js +89 -0
- package/dist/scripts/generate-manifest.js +155 -0
- package/dist/scripts/generate-test-manifest.js +77 -0
- package/dist/scripts/migrate-datetime-to-timestamp.ts +310 -0
- package/dist/scripts/prepack.js +49 -0
- package/dist/signals/bus.d.ts +64 -0
- package/dist/signals/bus.d.ts.map +1 -0
- package/dist/signals/bus.js +102 -0
- package/dist/signals/bus.js.map +1 -0
- package/dist/signals/index.d.ts +11 -0
- package/dist/signals/index.d.ts.map +1 -0
- package/dist/signals/sanitizer.d.ts +54 -0
- package/dist/signals/sanitizer.d.ts.map +1 -0
- package/dist/signals/sanitizer.js +111 -0
- package/dist/signals/sanitizer.js.map +1 -0
- package/dist/smrt-knowledge.json +335 -0
- package/dist/system/compatibility.d.ts +8 -0
- package/dist/system/compatibility.d.ts.map +1 -0
- package/dist/system/compatibility.js +409 -0
- package/dist/system/compatibility.js.map +1 -0
- package/dist/system/index.d.ts +9 -0
- package/dist/system/index.d.ts.map +1 -0
- package/dist/system/schema.d.ts +69 -0
- package/dist/system/schema.d.ts.map +1 -0
- package/dist/system/schema.js +271 -0
- package/dist/system/schema.js.map +1 -0
- package/dist/system/types.d.ts +135 -0
- package/dist/system/types.d.ts.map +1 -0
- package/dist/system-fields.d.ts +44 -0
- package/dist/system-fields.d.ts.map +1 -0
- package/dist/system-fields.js +55 -0
- package/dist/system-fields.js.map +1 -0
- package/dist/table-cache.d.ts +28 -0
- package/dist/table-cache.d.ts.map +1 -0
- package/dist/table-cache.js +21 -0
- package/dist/table-cache.js.map +1 -0
- package/dist/test-utils.d.ts +140 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/testing/database.d.ts +73 -0
- package/dist/testing/database.d.ts.map +1 -0
- package/dist/testing/database.js +204 -0
- package/dist/testing/database.js.map +1 -0
- package/dist/testing/index.d.ts +21 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing.d.ts +6 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +5 -0
- package/dist/testing.js.map +1 -0
- package/dist/tools/index.d.ts +8 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/tool-executor.d.ts +101 -0
- package/dist/tools/tool-executor.d.ts.map +1 -0
- package/dist/tools/tool-executor.js +142 -0
- package/dist/tools/tool-executor.js.map +1 -0
- package/dist/tools/tool-generator.d.ts +54 -0
- package/dist/tools/tool-generator.d.ts.map +1 -0
- package/dist/tools/tool-generator.js +121 -0
- package/dist/tools/tool-generator.js.map +1 -0
- package/dist/utils/chunk.d.ts +32 -0
- package/dist/utils/chunk.d.ts.map +1 -0
- package/dist/utils/chunk.js +14 -0
- package/dist/utils/chunk.js.map +1 -0
- package/dist/utils/import-workspace-module.d.ts +8 -0
- package/dist/utils/import-workspace-module.d.ts.map +1 -0
- package/dist/utils/import-workspace-module.js +81 -0
- package/dist/utils/import-workspace-module.js.map +1 -0
- package/dist/utils/json.d.ts +102 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +43 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/lru-cache.d.ts +69 -0
- package/dist/utils/lru-cache.d.ts.map +1 -0
- package/dist/utils/lru-cache.js +100 -0
- package/dist/utils/lru-cache.js.map +1 -0
- package/dist/utils/naming.d.ts +16 -0
- package/dist/utils/naming.d.ts.map +1 -0
- package/dist/utils/naming.js +23 -0
- package/dist/utils/naming.js.map +1 -0
- package/dist/utils/qualified-names.d.ts +122 -0
- package/dist/utils/qualified-names.d.ts.map +1 -0
- package/dist/utils/qualified-names.js +82 -0
- package/dist/utils/qualified-names.js.map +1 -0
- package/dist/utils/scanner-module.d.ts +37 -0
- package/dist/utils/scanner-module.d.ts.map +1 -0
- package/dist/utils.d.ts +177 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +185 -0
- package/dist/utils.js.map +1 -0
- package/dist/vite-plugin/import-build-aware.d.ts +68 -0
- package/dist/vite-plugin/import-build-aware.d.ts.map +1 -0
- package/dist/vite-plugin/import-build-aware.js +72 -0
- package/dist/vite-plugin/import-build-aware.js.map +1 -0
- package/dist/vite-plugin/index.d.ts +59 -0
- package/dist/vite-plugin/index.d.ts.map +1 -0
- package/dist/vite-plugin/index.js +1400 -0
- package/dist/vite-plugin/index.js.map +1 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts +66 -0
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -0
- package/dist/vite-plugin/sveltekit-generator.js +1375 -0
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -0
- package/dist/vite-plugin/templates/default-ui.ts +432 -0
- package/dist/vite-plugin/templates/default.html +206 -0
- package/dist/vite-plugin.d.ts +8 -0
- package/dist/vite-plugin.d.ts.map +1 -0
- package/dist/vite-plugin.js +11 -0
- package/dist/vite-plugin.js.map +1 -0
- package/package.json +208 -0
|
@@ -0,0 +1,1220 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve, dirname } from "node:path";
|
|
3
|
+
import { SmrtCollection } from "../collection.js";
|
|
4
|
+
import { ObjectRegistry } from "../registry.js";
|
|
5
|
+
import { generateRuntimeBootstrap, generateClaudeConfig, generateMCPDocumentation, generateMCPScript } from "./mcp-runtime-template.js";
|
|
6
|
+
import { runWithTenantGate } from "./tenant-gate.js";
|
|
7
|
+
class MCPGenerator {
|
|
8
|
+
config;
|
|
9
|
+
context;
|
|
10
|
+
collections = /* @__PURE__ */ new Map();
|
|
11
|
+
constructor(config = {}, context = {}) {
|
|
12
|
+
this.config = {
|
|
13
|
+
name: "smrt-mcp-server",
|
|
14
|
+
version: "1.0.0",
|
|
15
|
+
description: "Auto-generated MCP server from smrt objects",
|
|
16
|
+
server: {
|
|
17
|
+
name: "smrt-mcp",
|
|
18
|
+
version: "1.0.0"
|
|
19
|
+
},
|
|
20
|
+
...config
|
|
21
|
+
};
|
|
22
|
+
this.context = context;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get server name
|
|
26
|
+
*/
|
|
27
|
+
get name() {
|
|
28
|
+
return this.config.name;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get server version
|
|
32
|
+
*/
|
|
33
|
+
get version() {
|
|
34
|
+
return this.config.version;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generate all available tools from registered objects
|
|
38
|
+
*/
|
|
39
|
+
async generateTools() {
|
|
40
|
+
const tools = [];
|
|
41
|
+
const registeredClasses = ObjectRegistry.getAllClasses();
|
|
42
|
+
for (const [key, classInfo] of registeredClasses) {
|
|
43
|
+
const simpleName = classInfo.name || key;
|
|
44
|
+
const config = ObjectRegistry.getConfig(simpleName);
|
|
45
|
+
const mcpConfig = config.mcp;
|
|
46
|
+
if (mcpConfig === false) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const excluded = typeof mcpConfig === "object" && mcpConfig?.exclude ? mcpConfig.exclude : [];
|
|
50
|
+
const included = typeof mcpConfig === "object" ? mcpConfig?.include : void 0;
|
|
51
|
+
const shouldInclude = (endpoint) => {
|
|
52
|
+
if (included && !included.includes(endpoint)) return false;
|
|
53
|
+
if (excluded.includes(endpoint)) return false;
|
|
54
|
+
return true;
|
|
55
|
+
};
|
|
56
|
+
const objectTools = await this.generateObjectTools(
|
|
57
|
+
simpleName,
|
|
58
|
+
shouldInclude
|
|
59
|
+
);
|
|
60
|
+
tools.push(...objectTools);
|
|
61
|
+
}
|
|
62
|
+
return tools;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Generate tools for a specific object
|
|
66
|
+
*/
|
|
67
|
+
async generateObjectTools(objectName, shouldInclude) {
|
|
68
|
+
const tools = [];
|
|
69
|
+
const fields = ObjectRegistry.getFields(objectName);
|
|
70
|
+
const lowerName = objectName.toLowerCase();
|
|
71
|
+
const classInfo = ObjectRegistry.getClass(objectName);
|
|
72
|
+
if (shouldInclude("list")) {
|
|
73
|
+
tools.push({
|
|
74
|
+
name: `${lowerName}_list`,
|
|
75
|
+
description: `List ${objectName} objects with optional filtering`,
|
|
76
|
+
inputSchema: {
|
|
77
|
+
type: "object",
|
|
78
|
+
properties: {
|
|
79
|
+
limit: {
|
|
80
|
+
type: "integer",
|
|
81
|
+
description: "Maximum number of items to return",
|
|
82
|
+
default: 50,
|
|
83
|
+
minimum: 1,
|
|
84
|
+
maximum: 1e3
|
|
85
|
+
},
|
|
86
|
+
offset: {
|
|
87
|
+
type: "integer",
|
|
88
|
+
description: "Number of items to skip",
|
|
89
|
+
default: 0,
|
|
90
|
+
minimum: 0
|
|
91
|
+
},
|
|
92
|
+
orderBy: {
|
|
93
|
+
type: "string",
|
|
94
|
+
description: 'Field to order by (e.g., "created_at DESC")'
|
|
95
|
+
},
|
|
96
|
+
where: {
|
|
97
|
+
type: "object",
|
|
98
|
+
description: "Filter conditions as key-value pairs",
|
|
99
|
+
additionalProperties: true
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
if (shouldInclude("get")) {
|
|
106
|
+
tools.push({
|
|
107
|
+
name: `${lowerName}_get`,
|
|
108
|
+
description: `Get a specific ${objectName} by ID or slug`,
|
|
109
|
+
inputSchema: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
id: {
|
|
113
|
+
type: "string",
|
|
114
|
+
description: "Unique identifier of the object"
|
|
115
|
+
},
|
|
116
|
+
slug: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "URL-friendly identifier of the object"
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
required: ["id"]
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (shouldInclude("create")) {
|
|
126
|
+
const properties = {};
|
|
127
|
+
const required = [];
|
|
128
|
+
for (const [fieldName, field] of fields) {
|
|
129
|
+
properties[fieldName] = this.fieldToMCPSchema(field);
|
|
130
|
+
if (field._meta?.required) {
|
|
131
|
+
required.push(fieldName);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
tools.push({
|
|
135
|
+
name: `${lowerName}_create`,
|
|
136
|
+
description: `Create a new ${objectName}`,
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: "object",
|
|
139
|
+
properties,
|
|
140
|
+
required
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
if (shouldInclude("update")) {
|
|
145
|
+
const properties = {
|
|
146
|
+
id: {
|
|
147
|
+
type: "string",
|
|
148
|
+
description: "ID of the object to update"
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
for (const [fieldName, field] of fields) {
|
|
152
|
+
properties[fieldName] = this.fieldToMCPSchema(field);
|
|
153
|
+
}
|
|
154
|
+
tools.push({
|
|
155
|
+
name: `${lowerName}_update`,
|
|
156
|
+
description: `Update an existing ${objectName}`,
|
|
157
|
+
inputSchema: {
|
|
158
|
+
type: "object",
|
|
159
|
+
properties,
|
|
160
|
+
required: ["id"]
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (shouldInclude("delete")) {
|
|
165
|
+
tools.push({
|
|
166
|
+
name: `${lowerName}_delete`,
|
|
167
|
+
description: `Delete a ${objectName} by ID`,
|
|
168
|
+
inputSchema: {
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
id: {
|
|
172
|
+
type: "string",
|
|
173
|
+
description: "ID of the object to delete"
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
required: ["id"]
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (classInfo) {
|
|
181
|
+
const config = ObjectRegistry.getConfig(objectName);
|
|
182
|
+
const mcpConfig = config.mcp;
|
|
183
|
+
const included = typeof mcpConfig === "object" ? mcpConfig?.include : void 0;
|
|
184
|
+
const excluded = typeof mcpConfig === "object" && mcpConfig?.exclude ? mcpConfig.exclude : [];
|
|
185
|
+
const crudOperations = ["list", "get", "create", "update", "delete"];
|
|
186
|
+
const customMethodsInInclude = included?.filter((item) => !crudOperations.includes(item)) || [];
|
|
187
|
+
const hasIncludeList = included !== void 0;
|
|
188
|
+
const methods = await ObjectRegistry.getAllMethods(objectName);
|
|
189
|
+
const methodNames = new Set(Array.from(methods.keys()));
|
|
190
|
+
if (hasIncludeList) {
|
|
191
|
+
for (const methodName of customMethodsInInclude) {
|
|
192
|
+
if (excluded.includes(methodName)) continue;
|
|
193
|
+
const existsInManifest = methodNames.has(methodName);
|
|
194
|
+
const existsOnClass = this.validateCustomMethod(
|
|
195
|
+
classInfo.constructor,
|
|
196
|
+
methodName
|
|
197
|
+
);
|
|
198
|
+
if (!existsInManifest && !existsOnClass) {
|
|
199
|
+
console.warn(
|
|
200
|
+
`Warning: Custom action '${methodName}' specified in MCP config for ${objectName}, but method ${methodName}() not found on class`
|
|
201
|
+
);
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
const methodDef = methods.get(methodName);
|
|
205
|
+
if (methodDef && !methodDef.isPublic) continue;
|
|
206
|
+
const toolName = `${lowerName}_${methodName}`.toLowerCase();
|
|
207
|
+
tools.push({
|
|
208
|
+
name: toolName,
|
|
209
|
+
description: `Execute ${methodName} action on ${objectName}`,
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
id: {
|
|
214
|
+
type: "string",
|
|
215
|
+
description: "ID of the object to execute action on"
|
|
216
|
+
},
|
|
217
|
+
options: {
|
|
218
|
+
type: "object",
|
|
219
|
+
description: "Additional options for the custom action",
|
|
220
|
+
additionalProperties: true
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
required: ["id"]
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
for (const [methodName, methodDef] of methods) {
|
|
229
|
+
if (!methodDef.isPublic) continue;
|
|
230
|
+
if (excluded.includes(methodName)) continue;
|
|
231
|
+
const toolName = `${lowerName}_${methodName}`.toLowerCase();
|
|
232
|
+
tools.push({
|
|
233
|
+
name: toolName,
|
|
234
|
+
description: `Execute ${methodName} action on ${objectName}`,
|
|
235
|
+
inputSchema: {
|
|
236
|
+
type: "object",
|
|
237
|
+
properties: {
|
|
238
|
+
id: {
|
|
239
|
+
type: "string",
|
|
240
|
+
description: "ID of the object to execute action on"
|
|
241
|
+
},
|
|
242
|
+
options: {
|
|
243
|
+
type: "object",
|
|
244
|
+
description: "Additional options for the custom action",
|
|
245
|
+
additionalProperties: true
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
required: ["id"]
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return tools;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Validate that a custom method exists on a class
|
|
258
|
+
*/
|
|
259
|
+
validateCustomMethod(classConstructor, methodName) {
|
|
260
|
+
try {
|
|
261
|
+
const prototype = classConstructor.prototype;
|
|
262
|
+
if (typeof prototype[methodName] === "function") {
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
if (typeof classConstructor[methodName] === "function") {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
return false;
|
|
269
|
+
} catch (error) {
|
|
270
|
+
console.warn(
|
|
271
|
+
`Error validating method ${methodName} on class ${classConstructor.name}:`,
|
|
272
|
+
error
|
|
273
|
+
);
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Convert field definition to MCP schema
|
|
279
|
+
*/
|
|
280
|
+
fieldToMCPSchema(field) {
|
|
281
|
+
const schema = {
|
|
282
|
+
description: field._meta?.description || `${field.type} field`
|
|
283
|
+
};
|
|
284
|
+
switch (field.type) {
|
|
285
|
+
case "text":
|
|
286
|
+
schema.type = "string";
|
|
287
|
+
if (field._meta?.maxLength) schema.maxLength = field._meta.maxLength;
|
|
288
|
+
if (field._meta?.minLength) schema.minLength = field._meta.minLength;
|
|
289
|
+
break;
|
|
290
|
+
case "integer":
|
|
291
|
+
schema.type = "integer";
|
|
292
|
+
if (field._meta?.min !== void 0) schema.minimum = field._meta.min;
|
|
293
|
+
if (field._meta?.max !== void 0) schema.maximum = field._meta.max;
|
|
294
|
+
break;
|
|
295
|
+
case "decimal":
|
|
296
|
+
schema.type = "number";
|
|
297
|
+
if (field._meta?.min !== void 0) schema.minimum = field._meta.min;
|
|
298
|
+
if (field._meta?.max !== void 0) schema.maximum = field._meta.max;
|
|
299
|
+
break;
|
|
300
|
+
case "boolean":
|
|
301
|
+
schema.type = "boolean";
|
|
302
|
+
break;
|
|
303
|
+
case "datetime":
|
|
304
|
+
schema.type = "string";
|
|
305
|
+
schema.format = "date-time";
|
|
306
|
+
break;
|
|
307
|
+
case "json":
|
|
308
|
+
schema.type = "object";
|
|
309
|
+
break;
|
|
310
|
+
case "foreignKey":
|
|
311
|
+
schema.type = "string";
|
|
312
|
+
schema.description = `ID of related ${field.related || "object"}`;
|
|
313
|
+
break;
|
|
314
|
+
default:
|
|
315
|
+
schema.type = "string";
|
|
316
|
+
}
|
|
317
|
+
if (field._meta?.default !== void 0) {
|
|
318
|
+
schema.default = field._meta.default;
|
|
319
|
+
}
|
|
320
|
+
return schema;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Handle MCP tool calls
|
|
324
|
+
*/
|
|
325
|
+
async handleToolCall(request) {
|
|
326
|
+
const { name, arguments: args } = request.params;
|
|
327
|
+
try {
|
|
328
|
+
const availableTools = await this.generateTools();
|
|
329
|
+
const toolExists = availableTools.some((t) => t.name === name);
|
|
330
|
+
if (!toolExists) {
|
|
331
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
332
|
+
}
|
|
333
|
+
const firstUnderscore = name.indexOf("_");
|
|
334
|
+
const objectName = firstUnderscore === -1 ? "" : name.slice(0, firstUnderscore);
|
|
335
|
+
const action = firstUnderscore === -1 ? "" : name.slice(firstUnderscore + 1);
|
|
336
|
+
if (!objectName || !action) {
|
|
337
|
+
throw new Error(`Invalid tool name format: ${name}`);
|
|
338
|
+
}
|
|
339
|
+
const registeredClasses = ObjectRegistry.getAllClasses();
|
|
340
|
+
let classInfo = null;
|
|
341
|
+
let actualObjectName = "";
|
|
342
|
+
for (const [_key, info] of registeredClasses) {
|
|
343
|
+
const simpleName = info.name || _key;
|
|
344
|
+
if (simpleName.toLowerCase() === objectName.toLowerCase()) {
|
|
345
|
+
classInfo = info;
|
|
346
|
+
actualObjectName = simpleName;
|
|
347
|
+
break;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (!classInfo) {
|
|
351
|
+
throw new Error(`Object type '${objectName}' not found`);
|
|
352
|
+
}
|
|
353
|
+
const collection = await this.getCollection(actualObjectName, classInfo);
|
|
354
|
+
const result = await this.executeAction(
|
|
355
|
+
collection,
|
|
356
|
+
action,
|
|
357
|
+
args,
|
|
358
|
+
actualObjectName
|
|
359
|
+
);
|
|
360
|
+
return {
|
|
361
|
+
content: [
|
|
362
|
+
{
|
|
363
|
+
type: "text",
|
|
364
|
+
text: JSON.stringify(result, null, 2)
|
|
365
|
+
}
|
|
366
|
+
]
|
|
367
|
+
};
|
|
368
|
+
} catch (error) {
|
|
369
|
+
return {
|
|
370
|
+
content: [
|
|
371
|
+
{
|
|
372
|
+
type: "text",
|
|
373
|
+
text: `Error: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
374
|
+
}
|
|
375
|
+
]
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Get or create collection for an object
|
|
381
|
+
*/
|
|
382
|
+
async getCollection(objectName, classInfo) {
|
|
383
|
+
if (!this.collections.has(objectName)) {
|
|
384
|
+
if (!classInfo.collectionConstructor || typeof classInfo.collectionConstructor !== "function") {
|
|
385
|
+
throw new Error(
|
|
386
|
+
`No valid collection constructor found for ${objectName}`
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
const collection2 = new classInfo.collectionConstructor({
|
|
390
|
+
ai: this.context.ai,
|
|
391
|
+
db: this.context.db
|
|
392
|
+
});
|
|
393
|
+
if (!(collection2 instanceof SmrtCollection)) {
|
|
394
|
+
throw new Error(
|
|
395
|
+
`Collection for ${objectName} must extend SmrtCollection`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
await collection2.initialize();
|
|
399
|
+
this.collections.set(objectName, collection2);
|
|
400
|
+
}
|
|
401
|
+
const collection = this.collections.get(objectName);
|
|
402
|
+
if (!collection) {
|
|
403
|
+
throw new Error(`Collection for ${objectName} not found`);
|
|
404
|
+
}
|
|
405
|
+
return collection;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Serialize a tool-response payload, excluding sensitive fields (#1540).
|
|
409
|
+
* Recurses through arrays and plain objects so a SmrtObject nested inside a
|
|
410
|
+
* custom-action result (e.g. `{ item }`) is also stripped — `JSON.stringify`
|
|
411
|
+
* would otherwise call its `toJSON()`. Non-plain instances (Date, etc.) and
|
|
412
|
+
* primitives pass through unchanged; a cycle guard prevents infinite loops.
|
|
413
|
+
*/
|
|
414
|
+
toPublicData(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
415
|
+
if (value === null || typeof value !== "object") return value;
|
|
416
|
+
if (typeof value.toPublicJSON === "function") return value.toPublicJSON();
|
|
417
|
+
if (Array.isArray(value)) {
|
|
418
|
+
if (seen.has(value)) return value;
|
|
419
|
+
seen.add(value);
|
|
420
|
+
return value.map((entry) => this.toPublicData(entry, seen));
|
|
421
|
+
}
|
|
422
|
+
const proto = Object.getPrototypeOf(value);
|
|
423
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
424
|
+
if (seen.has(value)) return value;
|
|
425
|
+
seen.add(value);
|
|
426
|
+
const out = {};
|
|
427
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
428
|
+
out[key] = this.toPublicData(entry, seen);
|
|
429
|
+
}
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Mass-assignment guard (#1540): strip framework/server-managed and
|
|
434
|
+
* `@field({ readonly: true })` fields from a create/update body, and — when an
|
|
435
|
+
* `@smrt({ api: { writable: [...] } })` allowlist is set — intersect with it.
|
|
436
|
+
*/
|
|
437
|
+
applyWritablePolicy(objectName, data) {
|
|
438
|
+
if (!data || typeof data !== "object") {
|
|
439
|
+
return {};
|
|
440
|
+
}
|
|
441
|
+
const serverManaged = /* @__PURE__ */ new Set([
|
|
442
|
+
"id",
|
|
443
|
+
"tenantId",
|
|
444
|
+
"tenant_id",
|
|
445
|
+
"createdAt",
|
|
446
|
+
"created_at",
|
|
447
|
+
"updatedAt",
|
|
448
|
+
"updated_at"
|
|
449
|
+
]);
|
|
450
|
+
const readonly = /* @__PURE__ */ new Set();
|
|
451
|
+
let writable = null;
|
|
452
|
+
if (objectName) {
|
|
453
|
+
const apiConfig = ObjectRegistry.getConfig(objectName)?.api;
|
|
454
|
+
if (apiConfig && typeof apiConfig === "object" && Array.isArray(apiConfig.writable)) {
|
|
455
|
+
writable = apiConfig.writable;
|
|
456
|
+
}
|
|
457
|
+
for (const [name, def] of ObjectRegistry.getFields(objectName)) {
|
|
458
|
+
if (def && (def.readonly === true || def._meta?.readonly === true)) {
|
|
459
|
+
readonly.add(name);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const result = {};
|
|
464
|
+
for (const [key, value] of Object.entries(data)) {
|
|
465
|
+
if (key.startsWith("_")) continue;
|
|
466
|
+
if (serverManaged.has(key)) continue;
|
|
467
|
+
if (readonly.has(key)) continue;
|
|
468
|
+
if (writable && !writable.includes(key)) continue;
|
|
469
|
+
result[key] = value;
|
|
470
|
+
}
|
|
471
|
+
return result;
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Execute action on collection
|
|
475
|
+
*/
|
|
476
|
+
/**
|
|
477
|
+
* Fail-closed authorization for tool calls (#1540). Mutating tools
|
|
478
|
+
* (create/update/delete + custom actions) require an authenticated principal
|
|
479
|
+
* (`context.user`) unless the object opts out via `@smrt({ api: { public } })`.
|
|
480
|
+
* Reads are allowed when `public` is `true` or `'read'`.
|
|
481
|
+
*/
|
|
482
|
+
requireToolAuth(objectName, mutating) {
|
|
483
|
+
const apiConfig = objectName ? ObjectRegistry.getConfig(objectName)?.api : void 0;
|
|
484
|
+
const publicAccess = apiConfig && typeof apiConfig === "object" ? apiConfig.public : void 0;
|
|
485
|
+
if (publicAccess === true) return;
|
|
486
|
+
if (publicAccess === "read" && !mutating) return;
|
|
487
|
+
if (!this.context.user) {
|
|
488
|
+
throw new Error("Authentication required");
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
async executeAction(collection, action, args, objectName) {
|
|
492
|
+
const mutating = action !== "list" && action !== "get";
|
|
493
|
+
this.requireToolAuth(objectName, mutating);
|
|
494
|
+
return runWithTenantGate(
|
|
495
|
+
{
|
|
496
|
+
className: objectName,
|
|
497
|
+
tenantId: this.context.tenantId,
|
|
498
|
+
allowCrossTenant: this.context.allowCrossTenant,
|
|
499
|
+
surface: "MCP"
|
|
500
|
+
},
|
|
501
|
+
() => this.runAction(collection, action, args, objectName)
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Derive the set of tenant-scoped object names (lowercased simple names) from
|
|
506
|
+
* a generated tool list, for the emitted runtime template's tenant gate
|
|
507
|
+
* (#1554). Tool names are `objectname_action`.
|
|
508
|
+
*
|
|
509
|
+
* Detection uses ONLY tenancy's `isTenantScopedClass` (the authoritative
|
|
510
|
+
* source the interceptor uses; covers `@TenantScoped`), consulted via an
|
|
511
|
+
* optional dynamic import. We deliberately do NOT fall back to core's
|
|
512
|
+
* `ObjectRegistry.isTenantScoped`: a `@smrt({ tenantScoped })` model can exist
|
|
513
|
+
* in an app that has NOT installed `@happyvertical/smrt-tenancy`, and emitting
|
|
514
|
+
* the static `import { runTenantScopedEntryPoint } from '@happyvertical/smrt-tenancy'`
|
|
515
|
+
* gate for it would crash the generated server at import time. When tenancy is
|
|
516
|
+
* absent there is also nothing to enforce, so emitting no gate is correct.
|
|
517
|
+
*/
|
|
518
|
+
async tenantScopedObjectNames(tools) {
|
|
519
|
+
let isTenantScopedClass;
|
|
520
|
+
try {
|
|
521
|
+
const tenancySpecifier = "@happyvertical/smrt-tenancy";
|
|
522
|
+
const tenancy = await import(
|
|
523
|
+
/* @vite-ignore */
|
|
524
|
+
tenancySpecifier
|
|
525
|
+
);
|
|
526
|
+
isTenantScopedClass = tenancy.isTenantScopedClass;
|
|
527
|
+
} catch {
|
|
528
|
+
isTenantScopedClass = void 0;
|
|
529
|
+
}
|
|
530
|
+
if (typeof isTenantScopedClass !== "function") return [];
|
|
531
|
+
const scoped = /* @__PURE__ */ new Set();
|
|
532
|
+
for (const tool of tools) {
|
|
533
|
+
const [objectName] = tool.name.split("_");
|
|
534
|
+
if (!objectName) continue;
|
|
535
|
+
for (const [key, info] of ObjectRegistry.getAllClasses()) {
|
|
536
|
+
const simpleName = info.name || key;
|
|
537
|
+
if (simpleName.toLowerCase() === objectName.toLowerCase()) {
|
|
538
|
+
if (isTenantScopedClass(simpleName)) {
|
|
539
|
+
scoped.add(simpleName.toLowerCase());
|
|
540
|
+
}
|
|
541
|
+
break;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
return Array.from(scoped);
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Execute a resolved MCP action (CRUD or custom) against a collection. Always
|
|
549
|
+
* invoked inside the tenant gate established by {@link executeAction}.
|
|
550
|
+
*/
|
|
551
|
+
async runAction(collection, action, args, objectName) {
|
|
552
|
+
switch (action) {
|
|
553
|
+
case "list": {
|
|
554
|
+
const listOptions = {
|
|
555
|
+
limit: Math.min(args.limit || 50, 1e3),
|
|
556
|
+
offset: args.offset || 0
|
|
557
|
+
};
|
|
558
|
+
if (args.where) {
|
|
559
|
+
listOptions.where = args.where;
|
|
560
|
+
}
|
|
561
|
+
if (args.orderBy) {
|
|
562
|
+
listOptions.orderBy = args.orderBy;
|
|
563
|
+
}
|
|
564
|
+
const results = await collection.list(listOptions);
|
|
565
|
+
const total = await collection.count({ where: args.where || {} });
|
|
566
|
+
return {
|
|
567
|
+
data: results.map((result) => this.toPublicData(result)),
|
|
568
|
+
meta: {
|
|
569
|
+
total,
|
|
570
|
+
limit: listOptions.limit,
|
|
571
|
+
offset: listOptions.offset,
|
|
572
|
+
count: results.length
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
case "get": {
|
|
577
|
+
if (!args.id && !args.slug) {
|
|
578
|
+
throw new Error("Either id or slug is required");
|
|
579
|
+
}
|
|
580
|
+
const filter = args.id ? args.id : args.slug;
|
|
581
|
+
const item = await collection.get(filter);
|
|
582
|
+
if (!item) {
|
|
583
|
+
throw new Error("Object not found");
|
|
584
|
+
}
|
|
585
|
+
return this.toPublicData(item);
|
|
586
|
+
}
|
|
587
|
+
case "create": {
|
|
588
|
+
const createData = this.applyWritablePolicy(
|
|
589
|
+
objectName,
|
|
590
|
+
args
|
|
591
|
+
);
|
|
592
|
+
if (this.context.user) {
|
|
593
|
+
createData.created_by = this.context.user.id;
|
|
594
|
+
createData.owner_id = this.context.user.id;
|
|
595
|
+
}
|
|
596
|
+
const newItem = await collection.create(createData);
|
|
597
|
+
await newItem.save();
|
|
598
|
+
return this.toPublicData(newItem);
|
|
599
|
+
}
|
|
600
|
+
case "update": {
|
|
601
|
+
const { id } = args;
|
|
602
|
+
if (!id) {
|
|
603
|
+
throw new Error("ID is required for update");
|
|
604
|
+
}
|
|
605
|
+
const existing = await collection.get(id);
|
|
606
|
+
if (!existing) {
|
|
607
|
+
throw new Error("Object not found");
|
|
608
|
+
}
|
|
609
|
+
const updateData = this.applyWritablePolicy(objectName, args);
|
|
610
|
+
Object.assign(existing, updateData);
|
|
611
|
+
if (this.context.user) {
|
|
612
|
+
existing.updated_by = this.context.user.id;
|
|
613
|
+
}
|
|
614
|
+
await existing.save();
|
|
615
|
+
return this.toPublicData(existing);
|
|
616
|
+
}
|
|
617
|
+
case "delete": {
|
|
618
|
+
if (!args.id) {
|
|
619
|
+
throw new Error("ID is required for delete");
|
|
620
|
+
}
|
|
621
|
+
const toDelete = await collection.get(args.id);
|
|
622
|
+
if (!toDelete) {
|
|
623
|
+
throw new Error("Object not found");
|
|
624
|
+
}
|
|
625
|
+
await toDelete.delete();
|
|
626
|
+
return { success: true, message: "Object deleted successfully" };
|
|
627
|
+
}
|
|
628
|
+
default: {
|
|
629
|
+
const result = await this.executeCustomAction(collection, action, args);
|
|
630
|
+
return this.toPublicData(result);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Execute a custom action on a collection/object
|
|
636
|
+
*/
|
|
637
|
+
async executeCustomAction(collection, action, args) {
|
|
638
|
+
const { id, options = {}, ...directArgs } = args;
|
|
639
|
+
try {
|
|
640
|
+
if (id) {
|
|
641
|
+
const object = await collection.get(id);
|
|
642
|
+
if (!object) {
|
|
643
|
+
throw new Error("Object not found");
|
|
644
|
+
}
|
|
645
|
+
const objectWithMethods = object;
|
|
646
|
+
if (typeof objectWithMethods[action] === "function") {
|
|
647
|
+
const methodArgs = Object.keys(options).length > 0 ? options : directArgs;
|
|
648
|
+
const result = await objectWithMethods[action](methodArgs);
|
|
649
|
+
return result;
|
|
650
|
+
} else {
|
|
651
|
+
throw new Error(`Method '${action}' not found on object instance`);
|
|
652
|
+
}
|
|
653
|
+
} else {
|
|
654
|
+
if (typeof collection[action] === "function") {
|
|
655
|
+
const methodArgs = Object.keys(options).length > 0 ? options : directArgs;
|
|
656
|
+
const result = await collection[action](methodArgs);
|
|
657
|
+
return result;
|
|
658
|
+
} else {
|
|
659
|
+
throw new Error(
|
|
660
|
+
`Method '${action}' not found on collection. For object-specific actions, provide an 'id' parameter.`
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
} catch (error) {
|
|
665
|
+
throw new Error(
|
|
666
|
+
`Failed to execute custom action '${action}': ${error instanceof Error ? error.message : "Unknown error"}`
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* Generate MCP server info
|
|
672
|
+
*/
|
|
673
|
+
getServerInfo() {
|
|
674
|
+
return {
|
|
675
|
+
name: this.config.server?.name,
|
|
676
|
+
version: this.config.server?.version,
|
|
677
|
+
description: this.config.description
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Generate complete MCP server with stdio transport
|
|
682
|
+
*
|
|
683
|
+
* Creates a runnable Node.js script that exposes SMRT objects as MCP tools.
|
|
684
|
+
* The generated server includes:
|
|
685
|
+
* - Stdio transport integration
|
|
686
|
+
* - Tool registration from ObjectRegistry
|
|
687
|
+
* - Error handling and logging
|
|
688
|
+
* - Graceful shutdown
|
|
689
|
+
*
|
|
690
|
+
* @param options - Server generation options
|
|
691
|
+
* @returns Promise that resolves when all files are written
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* ```typescript
|
|
695
|
+
* const generator = new MCPGenerator({
|
|
696
|
+
* name: 'my-app',
|
|
697
|
+
* version: '1.0.0'
|
|
698
|
+
* });
|
|
699
|
+
*
|
|
700
|
+
* await generator.generateServer({
|
|
701
|
+
* outputPath: '.smrt/mcp-server/index.js',
|
|
702
|
+
* serverName: 'my-app-mcp',
|
|
703
|
+
* debug: true
|
|
704
|
+
* });
|
|
705
|
+
* ```
|
|
706
|
+
*/
|
|
707
|
+
async generateServer(options = {}) {
|
|
708
|
+
const {
|
|
709
|
+
outputPath = ".smrt/mcp-server/index.js",
|
|
710
|
+
serverName = this.config.name || "smrt-mcp-server",
|
|
711
|
+
serverVersion = this.config.version || "1.0.0",
|
|
712
|
+
debug = false,
|
|
713
|
+
generateClaudeConfigFile = false,
|
|
714
|
+
generateReadme = false,
|
|
715
|
+
modular = false
|
|
716
|
+
} = options;
|
|
717
|
+
const resolvedPath = resolve(process.cwd(), outputPath);
|
|
718
|
+
const outputDir = dirname(resolvedPath);
|
|
719
|
+
await mkdir(outputDir, { recursive: true });
|
|
720
|
+
if (modular) {
|
|
721
|
+
await this.generateModularServer(
|
|
722
|
+
outputDir,
|
|
723
|
+
serverName,
|
|
724
|
+
serverVersion,
|
|
725
|
+
debug
|
|
726
|
+
);
|
|
727
|
+
} else {
|
|
728
|
+
const tools = await this.generateTools();
|
|
729
|
+
const runtimeOptions = {
|
|
730
|
+
name: serverName,
|
|
731
|
+
version: serverVersion,
|
|
732
|
+
description: this.config.description,
|
|
733
|
+
config: this.config,
|
|
734
|
+
context: this.context,
|
|
735
|
+
debug,
|
|
736
|
+
tools,
|
|
737
|
+
tenantScopedObjects: await this.tenantScopedObjectNames(tools)
|
|
738
|
+
};
|
|
739
|
+
const serverCode = generateRuntimeBootstrap(runtimeOptions);
|
|
740
|
+
await writeFile(resolvedPath, serverCode, "utf-8");
|
|
741
|
+
console.log(`✅ Generated MCP server: ${resolvedPath}`);
|
|
742
|
+
}
|
|
743
|
+
if (generateClaudeConfigFile) {
|
|
744
|
+
const claudeConfig = generateClaudeConfig(serverName, resolvedPath);
|
|
745
|
+
const claudeConfigPath = resolve(outputDir, "claude-config.example.json");
|
|
746
|
+
await writeFile(
|
|
747
|
+
claudeConfigPath,
|
|
748
|
+
JSON.stringify(claudeConfig, null, 2),
|
|
749
|
+
"utf-8"
|
|
750
|
+
);
|
|
751
|
+
console.log(`✅ Generated Claude config example: ${claudeConfigPath}`);
|
|
752
|
+
}
|
|
753
|
+
if (generateReadme) {
|
|
754
|
+
const readme = generateMCPDocumentation(serverName, outputPath);
|
|
755
|
+
const readmePath = resolve(outputDir, "MCP-README.md");
|
|
756
|
+
await writeFile(readmePath, readme, "utf-8");
|
|
757
|
+
console.log(`✅ Generated MCP documentation: ${readmePath}`);
|
|
758
|
+
}
|
|
759
|
+
const mcpScript = generateMCPScript(outputPath);
|
|
760
|
+
console.log(`
|
|
761
|
+
📝 Add this to your package.json scripts:`);
|
|
762
|
+
console.log(` "mcp": "${mcpScript}"
|
|
763
|
+
`);
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Generate modular MCP server structure
|
|
767
|
+
*
|
|
768
|
+
* Creates separate files for tools, handlers, configuration, and main entry point.
|
|
769
|
+
* This makes the generated server easier to customize and extend.
|
|
770
|
+
*
|
|
771
|
+
* @param outputDir - Directory to generate files in
|
|
772
|
+
* @param serverName - Server name
|
|
773
|
+
* @param serverVersion - Server version
|
|
774
|
+
* @param debug - Enable debug logging
|
|
775
|
+
*/
|
|
776
|
+
async generateModularServer(outputDir, serverName, serverVersion, debug) {
|
|
777
|
+
const toolsDir = resolve(outputDir, "tools");
|
|
778
|
+
const handlersDir = resolve(outputDir, "handlers");
|
|
779
|
+
await mkdir(toolsDir, { recursive: true });
|
|
780
|
+
await mkdir(handlersDir, { recursive: true });
|
|
781
|
+
const configPath = resolve(outputDir, "config.ts");
|
|
782
|
+
const configCode = this.generateConfigFile(
|
|
783
|
+
serverName,
|
|
784
|
+
serverVersion,
|
|
785
|
+
debug
|
|
786
|
+
);
|
|
787
|
+
await writeFile(configPath, configCode, "utf-8");
|
|
788
|
+
console.log(`✅ Generated config: ${configPath}`);
|
|
789
|
+
const toolsPath = resolve(toolsDir, "index.ts");
|
|
790
|
+
const toolsCode = await this.generateToolsFile();
|
|
791
|
+
await writeFile(toolsPath, toolsCode, "utf-8");
|
|
792
|
+
console.log(`✅ Generated tools: ${toolsPath}`);
|
|
793
|
+
const handlersPath = resolve(handlersDir, "index.ts");
|
|
794
|
+
const tenantScopedObjects = await this.tenantScopedObjectNames(
|
|
795
|
+
await this.generateTools()
|
|
796
|
+
);
|
|
797
|
+
const handlersCode = await this.generateHandlersFile(tenantScopedObjects);
|
|
798
|
+
await writeFile(handlersPath, handlersCode, "utf-8");
|
|
799
|
+
console.log(`✅ Generated handlers: ${handlersPath}`);
|
|
800
|
+
const indexPath = resolve(outputDir, "index.js");
|
|
801
|
+
const indexCode = this.generateModularIndex();
|
|
802
|
+
await writeFile(indexPath, indexCode, "utf-8");
|
|
803
|
+
console.log(`✅ Generated MCP server: ${indexPath}`);
|
|
804
|
+
}
|
|
805
|
+
/**
|
|
806
|
+
* Generate configuration file for modular server
|
|
807
|
+
*/
|
|
808
|
+
generateConfigFile(serverName, serverVersion, debug) {
|
|
809
|
+
return `/**
|
|
810
|
+
* MCP Server Configuration
|
|
811
|
+
* Auto-generated by @happyvertical/smrt-core
|
|
812
|
+
*/
|
|
813
|
+
|
|
814
|
+
export const SERVER_NAME = ${JSON.stringify(serverName)};
|
|
815
|
+
export const SERVER_VERSION = ${JSON.stringify(serverVersion)};
|
|
816
|
+
export const SERVER_DESCRIPTION = ${JSON.stringify(this.config.description)};
|
|
817
|
+
export const DEBUG = ${debug};
|
|
818
|
+
`;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Generate tools definitions file for modular server
|
|
822
|
+
*/
|
|
823
|
+
async generateToolsFile() {
|
|
824
|
+
const tools = await this.generateTools();
|
|
825
|
+
return `/**
|
|
826
|
+
* MCP Tools Definitions
|
|
827
|
+
* Auto-generated from SMRT objects
|
|
828
|
+
*/
|
|
829
|
+
|
|
830
|
+
export const tools: Array<{
|
|
831
|
+
name: string;
|
|
832
|
+
description: string;
|
|
833
|
+
inputSchema: any;
|
|
834
|
+
}> = ${JSON.stringify(tools, null, 2)};
|
|
835
|
+
`;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Generate switch cases for tool execution
|
|
839
|
+
*/
|
|
840
|
+
async generateToolSwitchCases(indent = " ") {
|
|
841
|
+
const tools = await this.generateTools();
|
|
842
|
+
const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
843
|
+
const switchCases = tools.map((tool) => {
|
|
844
|
+
const [objectName, action] = tool.name.split("_");
|
|
845
|
+
switch (action) {
|
|
846
|
+
case "list":
|
|
847
|
+
return `${indent}case '${tool.name}': {
|
|
848
|
+
${indent} const limit = args.limit ?? 50;
|
|
849
|
+
${indent} const offset = args.offset ?? 0;
|
|
850
|
+
${indent} const where = args.where ?? {};
|
|
851
|
+
|
|
852
|
+
${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {
|
|
853
|
+
${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },
|
|
854
|
+
${indent} ai: aiConfig
|
|
855
|
+
${indent} });
|
|
856
|
+
|
|
857
|
+
${indent} const items = await collection.list({ where, limit, offset });
|
|
858
|
+
${indent} const itemsPublic = items.map((item) => item.toPublicJSON());
|
|
859
|
+
${indent} return { content: [{ type: 'text', text: JSON.stringify(itemsPublic) }] };
|
|
860
|
+
${indent}}`;
|
|
861
|
+
case "get":
|
|
862
|
+
return `${indent}case '${tool.name}': {
|
|
863
|
+
${indent} if (!args.id && !args.slug) {
|
|
864
|
+
${indent} throw new Error('Either id or slug is required');
|
|
865
|
+
${indent} }
|
|
866
|
+
|
|
867
|
+
${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {
|
|
868
|
+
${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },
|
|
869
|
+
${indent} ai: aiConfig
|
|
870
|
+
${indent} });
|
|
871
|
+
|
|
872
|
+
${indent} const filter = args.id || args.slug;
|
|
873
|
+
${indent} const item = await collection.get(filter);
|
|
874
|
+
|
|
875
|
+
${indent} if (!item) {
|
|
876
|
+
${indent} throw new Error('Object not found');
|
|
877
|
+
${indent} }
|
|
878
|
+
|
|
879
|
+
${indent} return { content: [{ type: 'text', text: JSON.stringify(item.toPublicJSON()) }] };
|
|
880
|
+
${indent}}`;
|
|
881
|
+
case "create":
|
|
882
|
+
return `${indent}case '${tool.name}': {
|
|
883
|
+
${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {
|
|
884
|
+
${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },
|
|
885
|
+
${indent} ai: aiConfig
|
|
886
|
+
${indent} });
|
|
887
|
+
|
|
888
|
+
${indent} const newItem = await collection.create(applyWritablePolicy('${capitalize(objectName)}', args));
|
|
889
|
+
${indent} await newItem.save();
|
|
890
|
+
|
|
891
|
+
${indent} return { content: [{ type: 'text', text: JSON.stringify(newItem.toPublicJSON()) }] };
|
|
892
|
+
${indent}}`;
|
|
893
|
+
case "update":
|
|
894
|
+
return `${indent}case '${tool.name}': {
|
|
895
|
+
${indent} const { id, ...updateData } = args;
|
|
896
|
+
${indent} if (!id) {
|
|
897
|
+
${indent} throw new Error('ID is required for update');
|
|
898
|
+
${indent} }
|
|
899
|
+
|
|
900
|
+
${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {
|
|
901
|
+
${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },
|
|
902
|
+
${indent} ai: aiConfig
|
|
903
|
+
${indent} });
|
|
904
|
+
|
|
905
|
+
${indent} const existing = await collection.get(id);
|
|
906
|
+
${indent} if (!existing) {
|
|
907
|
+
${indent} throw new Error('Object not found');
|
|
908
|
+
${indent} }
|
|
909
|
+
|
|
910
|
+
${indent} Object.assign(existing, applyWritablePolicy('${capitalize(objectName)}', updateData));
|
|
911
|
+
${indent} await existing.save();
|
|
912
|
+
|
|
913
|
+
${indent} return { content: [{ type: 'text', text: JSON.stringify(existing.toPublicJSON()) }] };
|
|
914
|
+
${indent}}`;
|
|
915
|
+
case "delete":
|
|
916
|
+
return `${indent}case '${tool.name}': {
|
|
917
|
+
${indent} if (!args.id) {
|
|
918
|
+
${indent} throw new Error('ID is required for delete');
|
|
919
|
+
${indent} }
|
|
920
|
+
|
|
921
|
+
${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {
|
|
922
|
+
${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },
|
|
923
|
+
${indent} ai: aiConfig
|
|
924
|
+
${indent} });
|
|
925
|
+
|
|
926
|
+
${indent} const toDelete = await collection.get(args.id);
|
|
927
|
+
${indent} if (!toDelete) {
|
|
928
|
+
${indent} throw new Error('Object not found');
|
|
929
|
+
${indent} }
|
|
930
|
+
|
|
931
|
+
${indent} await toDelete.delete();
|
|
932
|
+
|
|
933
|
+
${indent} return { content: [{ type: 'text', text: JSON.stringify({ success: true, message: 'Object deleted successfully' }) }] };
|
|
934
|
+
${indent}}`;
|
|
935
|
+
default:
|
|
936
|
+
return `${indent}case '${tool.name}': {
|
|
937
|
+
${indent} const { id, options = {}, ...directArgs } = args;
|
|
938
|
+
|
|
939
|
+
${indent} if (!id) {
|
|
940
|
+
${indent} throw new Error('ID is required for custom action ${action}');
|
|
941
|
+
${indent} }
|
|
942
|
+
|
|
943
|
+
${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {
|
|
944
|
+
${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },
|
|
945
|
+
${indent} ai: aiConfig
|
|
946
|
+
${indent} });
|
|
947
|
+
|
|
948
|
+
${indent} const object = await collection.get(id);
|
|
949
|
+
${indent} if (!object) {
|
|
950
|
+
${indent} throw new Error('Object not found');
|
|
951
|
+
${indent} }
|
|
952
|
+
|
|
953
|
+
${indent} if (typeof object['${action}'] !== 'function') {
|
|
954
|
+
${indent} throw new Error('Method ${action} not found on object');
|
|
955
|
+
${indent} }
|
|
956
|
+
|
|
957
|
+
${indent} const methodArgs = Object.keys(options).length > 0 ? options : directArgs;
|
|
958
|
+
${indent} const result = await object['${action}'](methodArgs);
|
|
959
|
+
|
|
960
|
+
${indent} return { content: [{ type: 'text', text: JSON.stringify(toPublicResult(result)) }] };
|
|
961
|
+
${indent}}`;
|
|
962
|
+
}
|
|
963
|
+
}).join("\n\n");
|
|
964
|
+
return switchCases;
|
|
965
|
+
}
|
|
966
|
+
/**
|
|
967
|
+
* Generate handlers file for modular server
|
|
968
|
+
*/
|
|
969
|
+
async generateHandlersFile(tenantScopedObjects = []) {
|
|
970
|
+
const switchCases = await this.generateToolSwitchCases(" ");
|
|
971
|
+
const tenantScopedSet = Array.from(
|
|
972
|
+
new Set(tenantScopedObjects.map((n) => n.toLowerCase()))
|
|
973
|
+
);
|
|
974
|
+
const hasTenantScoped = tenantScopedSet.length > 0;
|
|
975
|
+
return `/**
|
|
976
|
+
* MCP Tool Call Handlers
|
|
977
|
+
* Auto-generated from SMRT objects
|
|
978
|
+
*
|
|
979
|
+
* SECURITY (#1540): responses exclude @field({ sensitive }) fields and
|
|
980
|
+
* create/update bodies are mass-assignment guarded. This handler has no
|
|
981
|
+
* per-call authentication principal — the generated stdio MCP server's trust
|
|
982
|
+
* boundary is the host process / MCP client. Run it only in a trusted context
|
|
983
|
+
* or behind an authenticated gateway.
|
|
984
|
+
*
|
|
985
|
+
* SECURITY (#1554): tenant-scoped tools run inside a fail-closed tenant gate;
|
|
986
|
+
* the tenant is taken from SMRT_MCP_TENANT_ID (this server has no auth
|
|
987
|
+
* principal) and tenancy is enabled so the interceptor enforces filtering.
|
|
988
|
+
*/
|
|
989
|
+
|
|
990
|
+
import { ObjectRegistry } from '@happyvertical/smrt-core';
|
|
991
|
+
${hasTenantScoped ? "import { enableTenancy, runTenantScopedEntryPoint } from '@happyvertical/smrt-tenancy';\n" : ""}${hasTenantScoped ? `
|
|
992
|
+
// Install the tenancy interceptor so tenant-scoped tools are filtered and the
|
|
993
|
+
// gate fail-closes when no tenant is supplied (#1554).
|
|
994
|
+
enableTenancy();
|
|
995
|
+
const TENANT_SCOPED = new Set(${JSON.stringify(tenantScopedSet)});
|
|
996
|
+
const MCP_TENANT_ID = process.env.SMRT_MCP_TENANT_ID || undefined;
|
|
997
|
+
const MCP_ALLOW_CROSS_TENANT = process.env.SMRT_MCP_ALLOW_CROSS_TENANT === 'true';
|
|
998
|
+
` : ""}
|
|
999
|
+
|
|
1000
|
+
/**
|
|
1001
|
+
* Mass-assignment guard (#1540): strip framework/server-managed and
|
|
1002
|
+
* \`@field({ readonly: true })\` fields from create/update bodies, intersecting
|
|
1003
|
+
* with the optional \`@smrt({ api: { writable: [...] } })\` allowlist.
|
|
1004
|
+
*/
|
|
1005
|
+
function applyWritablePolicy(objectName: string, data: any): Record<string, any> {
|
|
1006
|
+
if (!data || typeof data !== 'object') return {};
|
|
1007
|
+
const serverManaged = new Set<string>([
|
|
1008
|
+
'id', 'tenantId', 'tenant_id',
|
|
1009
|
+
'createdAt', 'created_at', 'updatedAt', 'updated_at',
|
|
1010
|
+
]);
|
|
1011
|
+
const readonly = new Set<string>();
|
|
1012
|
+
let writable: string[] | null = null;
|
|
1013
|
+
const apiConfig = ObjectRegistry.getConfig(objectName)?.api as any;
|
|
1014
|
+
if (apiConfig && typeof apiConfig === 'object' && Array.isArray(apiConfig.writable)) {
|
|
1015
|
+
writable = apiConfig.writable;
|
|
1016
|
+
}
|
|
1017
|
+
for (const [name, def] of ObjectRegistry.getFields(objectName)) {
|
|
1018
|
+
if (def && ((def as any).readonly === true || (def as any)._meta?.readonly === true)) {
|
|
1019
|
+
readonly.add(name);
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
const result: Record<string, any> = {};
|
|
1023
|
+
for (const [key, value] of Object.entries(data as Record<string, any>)) {
|
|
1024
|
+
if (key.startsWith('_')) continue;
|
|
1025
|
+
if (serverManaged.has(key)) continue;
|
|
1026
|
+
if (readonly.has(key)) continue;
|
|
1027
|
+
if (writable && !writable.includes(key)) continue;
|
|
1028
|
+
result[key] = value;
|
|
1029
|
+
}
|
|
1030
|
+
return result;
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
/**
|
|
1034
|
+
* Sensitive-field-safe serialization for custom-action results (#1540).
|
|
1035
|
+
* Recurses through arrays and plain objects so nested SmrtObjects are stripped
|
|
1036
|
+
* too; non-plain instances (Date, etc.) and primitives pass through. Cycle-safe.
|
|
1037
|
+
*/
|
|
1038
|
+
function toPublicResult(value: any, seen: WeakSet<object> = new WeakSet()): any {
|
|
1039
|
+
if (value === null || typeof value !== 'object') return value;
|
|
1040
|
+
if (typeof value.toPublicJSON === 'function') return value.toPublicJSON();
|
|
1041
|
+
if (Array.isArray(value)) {
|
|
1042
|
+
if (seen.has(value)) return value;
|
|
1043
|
+
seen.add(value);
|
|
1044
|
+
return value.map((entry: any) => toPublicResult(entry, seen));
|
|
1045
|
+
}
|
|
1046
|
+
const proto = Object.getPrototypeOf(value);
|
|
1047
|
+
if (proto !== Object.prototype && proto !== null) return value;
|
|
1048
|
+
if (seen.has(value)) return value;
|
|
1049
|
+
seen.add(value);
|
|
1050
|
+
const out: Record<string, any> = {};
|
|
1051
|
+
for (const [key, entry] of Object.entries(value as Record<string, any>)) {
|
|
1052
|
+
out[key] = toPublicResult(entry, seen);
|
|
1053
|
+
}
|
|
1054
|
+
return out;
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Handle tool call request
|
|
1059
|
+
*/
|
|
1060
|
+
export async function handleToolCall(
|
|
1061
|
+
name: string,
|
|
1062
|
+
arguments: any = {},
|
|
1063
|
+
aiConfig: any = {}
|
|
1064
|
+
) {
|
|
1065
|
+
try {
|
|
1066
|
+
const args = arguments;
|
|
1067
|
+
|
|
1068
|
+
const runToolBody = async () => {
|
|
1069
|
+
switch (name) {
|
|
1070
|
+
${switchCases}
|
|
1071
|
+
|
|
1072
|
+
default:
|
|
1073
|
+
throw new Error(\`Unknown tool: \${name}\`);
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
${hasTenantScoped ? `
|
|
1077
|
+
// Fail-closed tenant context for tenant-scoped tools (#1554).
|
|
1078
|
+
const [toolObject] = name.split('_');
|
|
1079
|
+
const result =
|
|
1080
|
+
toolObject && TENANT_SCOPED.has(toolObject.toLowerCase())
|
|
1081
|
+
? await runTenantScopedEntryPoint(
|
|
1082
|
+
{ tenantScoped: true, tenantId: MCP_TENANT_ID, allowCrossTenant: MCP_ALLOW_CROSS_TENANT, surface: 'MCP' },
|
|
1083
|
+
runToolBody,
|
|
1084
|
+
)
|
|
1085
|
+
: await runToolBody();` : `
|
|
1086
|
+
const result = await runToolBody();`}
|
|
1087
|
+
|
|
1088
|
+
return result;
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
1091
|
+
|
|
1092
|
+
return {
|
|
1093
|
+
content: [
|
|
1094
|
+
{
|
|
1095
|
+
type: 'text',
|
|
1096
|
+
text: \`Error executing tool \${name}: \${errorMessage}\`,
|
|
1097
|
+
},
|
|
1098
|
+
],
|
|
1099
|
+
isError: true,
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
`;
|
|
1104
|
+
}
|
|
1105
|
+
/**
|
|
1106
|
+
* Generate modular index file (main entry point)
|
|
1107
|
+
*/
|
|
1108
|
+
generateModularIndex() {
|
|
1109
|
+
return `#!/usr/bin/env node
|
|
1110
|
+
/**
|
|
1111
|
+
* Auto-generated MCP Server
|
|
1112
|
+
* Generated by @happyvertical/smrt-core MCPGenerator
|
|
1113
|
+
*
|
|
1114
|
+
* This server exposes SMRT objects as MCP tools for AI integration.
|
|
1115
|
+
*/
|
|
1116
|
+
|
|
1117
|
+
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
1118
|
+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
1119
|
+
import {
|
|
1120
|
+
CallToolRequestSchema,
|
|
1121
|
+
ListToolsRequestSchema,
|
|
1122
|
+
} from '@modelcontextprotocol/sdk/types.js';
|
|
1123
|
+
import { config } from '@happyvertical/smrt-config';
|
|
1124
|
+
import { getDatabase } from '@happyvertical/sql';
|
|
1125
|
+
import { getAI } from '@happyvertical/ai';
|
|
1126
|
+
|
|
1127
|
+
import { SERVER_NAME, SERVER_VERSION, DEBUG } from './config.js';
|
|
1128
|
+
import { tools } from './tools/index.js';
|
|
1129
|
+
import { handleToolCall } from './handlers/index.js';
|
|
1130
|
+
|
|
1131
|
+
/**
|
|
1132
|
+
* Main server startup function
|
|
1133
|
+
*/
|
|
1134
|
+
async function main() {
|
|
1135
|
+
try {
|
|
1136
|
+
if (DEBUG) {
|
|
1137
|
+
console.error(\`[MCP] Starting server: \${SERVER_NAME} v\${SERVER_VERSION}\`);
|
|
1138
|
+
console.error(\`[MCP] Available tools:\`, tools.map(t => t.name).join(', '));
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// Load configuration from environment and .smrt.config files
|
|
1142
|
+
const appConfig = await config.load();
|
|
1143
|
+
const aiConfig = appConfig?.ai || {};
|
|
1144
|
+
|
|
1145
|
+
// Create MCP server
|
|
1146
|
+
const server = new Server(
|
|
1147
|
+
{
|
|
1148
|
+
name: SERVER_NAME,
|
|
1149
|
+
version: SERVER_VERSION,
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
capabilities: {
|
|
1153
|
+
tools: {},
|
|
1154
|
+
},
|
|
1155
|
+
}
|
|
1156
|
+
);
|
|
1157
|
+
|
|
1158
|
+
// Register ListTools handler
|
|
1159
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
1160
|
+
if (DEBUG) {
|
|
1161
|
+
console.error(\`[MCP] ListTools request received\`);
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
return {
|
|
1165
|
+
tools: tools.map(tool => ({
|
|
1166
|
+
name: tool.name,
|
|
1167
|
+
description: tool.description,
|
|
1168
|
+
inputSchema: tool.inputSchema,
|
|
1169
|
+
})),
|
|
1170
|
+
};
|
|
1171
|
+
});
|
|
1172
|
+
|
|
1173
|
+
// Register CallTool handler
|
|
1174
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
1175
|
+
const { name, arguments: args = {} } = request.params;
|
|
1176
|
+
|
|
1177
|
+
if (DEBUG) {
|
|
1178
|
+
console.error(\`[MCP] CallTool request: \${name}\`);
|
|
1179
|
+
console.error(\`[MCP] Arguments:\`, JSON.stringify(args, null, 2));
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
return await handleToolCall(name, args, aiConfig);
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
// Setup stdio transport
|
|
1186
|
+
const transport = new StdioServerTransport();
|
|
1187
|
+
await server.connect(transport);
|
|
1188
|
+
|
|
1189
|
+
if (DEBUG) {
|
|
1190
|
+
console.error(\`[MCP] Server connected and ready\`);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
// Handle graceful shutdown
|
|
1194
|
+
const shutdown = async () => {
|
|
1195
|
+
if (DEBUG) {
|
|
1196
|
+
console.error(\`[MCP] Shutting down gracefully\`);
|
|
1197
|
+
}
|
|
1198
|
+
await server.close();
|
|
1199
|
+
process.exit(0);
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
process.on('SIGINT', shutdown);
|
|
1203
|
+
process.on('SIGTERM', shutdown);
|
|
1204
|
+
} catch (error) {
|
|
1205
|
+
console.error('[MCP] Fatal error during server startup:', error);
|
|
1206
|
+
process.exit(1);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
main().catch((error) => {
|
|
1211
|
+
console.error('[MCP] Unhandled error:', error);
|
|
1212
|
+
process.exit(1);
|
|
1213
|
+
});
|
|
1214
|
+
`;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
export {
|
|
1218
|
+
MCPGenerator
|
|
1219
|
+
};
|
|
1220
|
+
//# sourceMappingURL=mcp.js.map
|