@happyvertical/smrt-core 0.36.5 → 0.36.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/collection.d.ts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/consumer-plugin/index.d.ts.map +1 -1
- package/dist/consumer-plugin/index.js +7 -3
- package/dist/consumer-plugin/index.js.map +1 -1
- package/dist/database.d.ts +3 -3
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js.map +1 -1
- package/dist/embeddings/provider.d.ts +14 -2
- package/dist/embeddings/provider.d.ts.map +1 -1
- package/dist/embeddings/provider.js +3 -3
- package/dist/embeddings/provider.js.map +1 -1
- package/dist/embeddings/storage.js.map +1 -1
- package/dist/errors.d.ts +22 -22
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +5 -4
- package/dist/errors.js.map +1 -1
- package/dist/generators/cli.d.ts +4 -22
- package/dist/generators/cli.d.ts.map +1 -1
- package/dist/generators/cli.js +9 -5
- package/dist/generators/cli.js.map +1 -1
- package/dist/generators/mcp-runtime-template.d.ts +1 -1
- package/dist/generators/mcp-runtime-template.d.ts.map +1 -1
- package/dist/generators/mcp-runtime-template.js.map +1 -1
- package/dist/generators/mcp.d.ts +16 -4
- package/dist/generators/mcp.d.ts.map +1 -1
- package/dist/generators/mcp.js +25 -9
- package/dist/generators/mcp.js.map +1 -1
- package/dist/generators/rest.d.ts +6 -5
- package/dist/generators/rest.d.ts.map +1 -1
- package/dist/generators/rest.js +8 -5
- package/dist/generators/rest.js.map +1 -1
- package/dist/generators/swagger.d.ts +12 -2
- package/dist/generators/swagger.d.ts.map +1 -1
- package/dist/generators/swagger.js +6 -3
- package/dist/generators/swagger.js.map +1 -1
- package/dist/knowledge.d.ts +12 -1
- package/dist/knowledge.d.ts.map +1 -1
- package/dist/knowledge.js.map +1 -1
- package/dist/lazy-config.d.ts.map +1 -1
- package/dist/lazy-config.js.map +1 -1
- package/dist/manifest/generator.d.ts.map +1 -1
- package/dist/manifest/generator.js +14 -12
- package/dist/manifest/generator.js.map +1 -1
- package/dist/manifest/manager.d.ts +3 -3
- package/dist/manifest/manager.d.ts.map +1 -1
- package/dist/manifest/manager.js.map +1 -1
- package/dist/manifest/manifest-loader.d.ts +3 -2
- package/dist/manifest/manifest-loader.d.ts.map +1 -1
- package/dist/manifest/manifest-loader.js +3 -2
- package/dist/manifest/manifest-loader.js.map +1 -1
- package/dist/manifest/static-manifest.js +2 -2
- package/dist/manifest/static-manifest.js.map +1 -1
- package/dist/manifest/store.js +2 -2
- package/dist/manifest/test-manifest-stub.js +2 -2
- package/dist/manifest/test-manifest-stub.js.map +1 -1
- package/dist/manifest.json +2 -2
- package/dist/mcp-advisor/index.d.ts +1 -1
- package/dist/mcp-advisor/index.d.ts.map +1 -1
- package/dist/mcp-advisor/tools/get-object-config.d.ts.map +1 -1
- package/dist/mcp-advisor/types.d.ts +5 -5
- package/dist/mcp-advisor/types.d.ts.map +1 -1
- package/dist/migrations/differ.js.map +1 -1
- package/dist/scanner/manifest-generator.d.ts +11 -3
- package/dist/scanner/manifest-generator.d.ts.map +1 -1
- package/dist/scanner/manifest-generator.js +19 -15
- package/dist/scanner/manifest-generator.js.map +1 -1
- package/dist/scanner/types.d.ts +60 -6
- package/dist/scanner/types.d.ts.map +1 -1
- package/dist/schema/code-generator.d.ts.map +1 -1
- package/dist/schema/ddl/base-strategy.d.ts +2 -2
- package/dist/schema/ddl/base-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/base-strategy.js.map +1 -1
- package/dist/schema/ddl/sqlite-strategy.d.ts +1 -1
- package/dist/schema/ddl/sqlite-strategy.d.ts.map +1 -1
- package/dist/schema/ddl/sqlite-strategy.js.map +1 -1
- package/dist/schema/ddl/types.d.ts +1 -1
- package/dist/schema/ddl/types.d.ts.map +1 -1
- package/dist/schema/generator.d.ts +27 -4
- package/dist/schema/generator.d.ts.map +1 -1
- package/dist/schema/generator.js +3 -2
- package/dist/schema/generator.js.map +1 -1
- package/dist/schema/override-system.d.ts +3 -3
- package/dist/schema/override-system.d.ts.map +1 -1
- package/dist/schema/schema-aggregator.d.ts.map +1 -1
- package/dist/schema/schema-manager.d.ts.map +1 -1
- package/dist/schema/schema-manager.js +12 -4
- package/dist/schema/schema-manager.js.map +1 -1
- package/dist/schema/types.d.ts +1 -1
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/utils.d.ts +6 -1
- package/dist/schema/utils.d.ts.map +1 -1
- package/dist/schema/utils.js +2 -1
- package/dist/schema/utils.js.map +1 -1
- package/dist/signals/sanitizer.d.ts +1 -1
- package/dist/signals/sanitizer.d.ts.map +1 -1
- package/dist/signals/sanitizer.js +12 -2
- package/dist/signals/sanitizer.js.map +1 -1
- package/dist/smrt-knowledge.json +4 -4
- package/dist/system/types.d.ts +2 -2
- package/dist/system/types.d.ts.map +1 -1
- package/dist/system-fields.d.ts +5 -43
- package/dist/system-fields.d.ts.map +1 -1
- package/dist/system-fields.js +2 -1
- package/dist/system-fields.js.map +1 -1
- package/dist/test-utils.d.ts +39 -13
- package/dist/test-utils.d.ts.map +1 -1
- package/dist/testing/database.d.ts.map +1 -1
- package/dist/testing/database.js.map +1 -1
- package/dist/tools/tool-executor.d.ts +19 -5
- package/dist/tools/tool-executor.d.ts.map +1 -1
- package/dist/tools/tool-executor.js +4 -2
- package/dist/tools/tool-executor.js.map +1 -1
- package/dist/tools/tool-generator.d.ts +8 -1
- package/dist/tools/tool-generator.d.ts.map +1 -1
- package/dist/tools/tool-generator.js +10 -11
- package/dist/tools/tool-generator.js.map +1 -1
- package/dist/utils/json.js.map +1 -1
- package/dist/utils.d.ts +16 -8
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/vite-plugin/index.d.ts.map +1 -1
- package/dist/vite-plugin/index.js +9 -7
- package/dist/vite-plugin/index.js.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.d.ts.map +1 -1
- package/dist/vite-plugin/sveltekit-generator.js +4 -3
- package/dist/vite-plugin/sveltekit-generator.js.map +1 -1
- package/dist/vite-plugin/templates/default-ui.ts +20 -6
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../../src/generators/cli.ts"],"sourcesContent":["/**\n * CLI command generator for smrt objects\n *\n * Exposes registered `@smrt()` objects as a runnable admin CLI: each object gets\n * `list`/`get`/`create`/`update`/`delete` commands plus its public custom\n * methods, dispatched as `objectname:action`.\n *\n * Security parity with the REST/MCP generators (#1540, #1547, #1554, #1556):\n * - **Mass-assignment guard** — create/update bodies are filtered through the\n * `@smrt({ api: { writable: [...] } })` allowlist, dropping server-managed and\n * `@field({ readonly })` fields ({@link CLIGenerator.applyWritablePolicy}).\n * - **Exhaustive include** — an `include` list is the COMPLETE allowlist for the\n * surface; custom methods are gated on `isPublic`.\n * - **Sensitive redaction** — command *output* is serialized through\n * {@link CLIGenerator.toPublicData} so `@field({ sensitive })` values never\n * print (input bodies are guarded by the writable allowlist above).\n * - **Fail-closed tenant context** — tenant-scoped reads/writes run inside the\n * tenancy gate; without `--tenant <id>` / `--all-tenants` (and with tenancy\n * enabled) the command throws rather than ranging across all tenants.\n */\n\nimport type { SmrtCollection } from '../collection';\nimport { ObjectRegistry } from '../registry';\nimport { runWithTenantGate } from './tenant-gate.js';\n\n/**\n * Configuration for a generated CLI.\n */\nexport interface CLIConfig {\n /** CLI program name (used in help output). */\n name?: string;\n /** CLI version. */\n version?: string;\n /** CLI description. */\n description?: string;\n}\n\n/**\n * Per-invocation context for a generated CLI.\n */\nexport interface CLIContext {\n /** Database handle passed to collections. */\n db?: any;\n /** AI provider passed to collections. */\n ai?: any;\n /** Authenticated operator, when the host establishes one. */\n user?: {\n id: string;\n roles?: string[];\n };\n /**\n * Default tenant for tenant-scoped commands when no `--tenant` flag is given.\n * Hosts that authenticate an operator may set this from the principal.\n */\n tenantId?: string;\n /**\n * Default cross-tenant opt-in when no `--all-tenants` flag is given. Hosts\n * may set this for trusted operator/admin shells.\n */\n allowCrossTenant?: boolean;\n}\n\n/** Standard CRUD verbs handled directly by the generator. */\nconst CRUD_OPERATIONS = ['list', 'get', 'create', 'update', 'delete'];\n\n/**\n * Flags consumed by the CLI itself — never treated as create/update field\n * values or as custom-action options.\n */\nconst RESERVED_FLAGS = new Set([\n 'tenant',\n 'all-tenants',\n 'from-file',\n 'limit',\n 'offset',\n 'order-by',\n 'orderBy',\n 'where',\n 'format',\n 'json',\n 'help',\n 'id',\n]);\n\n/** Parsed CLI invocation. */\ninterface ParsedInvocation {\n /** Object/collection segment (lowercased), e.g. `product`. */\n objectName: string;\n /** Action segment, e.g. `list` or a custom method name. */\n action: string;\n /** First positional argument after the command (typically an id). */\n positional?: string;\n /** Parsed flags. String for `--k v`/`--k=v`, `true` for bare `--flag`. */\n flags: Record<string, string | boolean>;\n}\n\n/**\n * Generate and run an admin CLI for the registered `@smrt()` objects.\n */\nexport class CLIGenerator {\n private config: CLIConfig;\n private context: CLIContext;\n\n constructor(config: CLIConfig = {}, context: CLIContext = {}) {\n this.config = {\n name: 'smrt-cli',\n version: '1.0.0',\n description: 'Auto-generated CLI from smrt objects',\n ...config,\n };\n this.context = context;\n }\n\n /** CLI program name. */\n get name(): string | undefined {\n return this.config.name;\n }\n\n /** CLI version. */\n get version(): string | undefined {\n return this.config.version;\n }\n\n /**\n * Return the argv handler invoked by the generated `setupCLI()` wrapper.\n *\n * @returns An async function accepting the post-`node script` argv slice.\n */\n generateHandler(): (args: string[]) => Promise<void> {\n return async (args: string[]) => {\n await this.run(args ?? []);\n };\n }\n\n /**\n * Parse and execute a single CLI invocation.\n *\n * @param args - argv slice (command + flags).\n */\n async run(args: string[]): Promise<void> {\n if (args.length === 0 || args[0] === '--help' || args[0] === 'help') {\n console.log(await this.helpText());\n return;\n }\n\n const parsed = this.parseArgs(args);\n if (!parsed) {\n console.log(await this.helpText());\n return;\n }\n\n const classInfo = this.resolveClass(parsed.objectName);\n if (!classInfo) {\n throw new Error(`Object type '${parsed.objectName}' not found`);\n }\n const objectName: string = classInfo.name || parsed.objectName;\n\n await this.assertCommandExposed(objectName, parsed.action);\n\n const tenantId =\n typeof parsed.flags.tenant === 'string'\n ? parsed.flags.tenant\n : this.context.tenantId;\n const allowCrossTenant =\n parsed.flags['all-tenants'] === true ||\n this.context.allowCrossTenant === true;\n\n // Tenant-scoping is resolved authoritatively inside tenancy (by class name),\n // matching the interceptor and covering both @TenantScoped and\n // @smrt({ tenantScoped }) registrations (#1554).\n const result = await runWithTenantGate(\n { className: objectName, tenantId, allowCrossTenant, surface: 'CLI' },\n async () => {\n const collection = await this.getCollection(objectName);\n return this.executeAction(collection, objectName, parsed);\n },\n );\n\n if (result !== undefined) {\n console.log(JSON.stringify(this.toPublicData(result), null, 2));\n }\n }\n\n /**\n * Parse argv into a command + flags. Supports `name:action` and\n * `name action` command forms, `--key value`, `--key=value`, and bare\n * `--flag` booleans.\n */\n private parseArgs(args: string[]): ParsedInvocation | null {\n const [command, ...rest] = args;\n if (!command || command.startsWith('-')) return null;\n\n let objectName: string;\n let action: string;\n let tail = rest;\n\n if (command.includes(':')) {\n const [obj, act] = command.split(':');\n objectName = obj.toLowerCase();\n action = act;\n } else if (rest.length > 0 && !rest[0].startsWith('-')) {\n objectName = command.toLowerCase();\n action = rest[0];\n tail = rest.slice(1);\n } else {\n // A bare object name with no action is not a runnable command.\n return null;\n }\n\n if (!objectName || !action) return null;\n\n const flags: Record<string, string | boolean> = {};\n let positional: string | undefined;\n\n for (let i = 0; i < tail.length; i++) {\n const token = tail[i];\n if (token.startsWith('--')) {\n const body = token.slice(2);\n const eq = body.indexOf('=');\n if (eq !== -1) {\n flags[body.slice(0, eq)] = body.slice(eq + 1);\n } else {\n const next = tail[i + 1];\n if (next !== undefined && !next.startsWith('--')) {\n flags[body] = next;\n i++;\n } else {\n flags[body] = true;\n }\n }\n } else if (positional === undefined) {\n positional = token;\n }\n }\n\n return { objectName, action, positional, flags };\n }\n\n /**\n * Resolve a registered class by simple name (case-insensitive), mirroring the\n * MCP generator's lookup.\n */\n private resolveClass(objectName: string): any {\n const registeredClasses = ObjectRegistry.getAllClasses();\n for (const [key, info] of registeredClasses) {\n const simpleName = info.name || key;\n if (simpleName.toLowerCase() === objectName.toLowerCase()) {\n return info;\n }\n }\n return null;\n }\n\n /**\n * Throw if `action` is not exposed for `objectName` under its `@smrt({ cli })`\n * config. `cli: false` disables the object entirely; an `include` list is the\n * complete allowlist; custom methods must be `isPublic`.\n */\n private async assertCommandExposed(\n objectName: string,\n action: string,\n ): Promise<void> {\n const config = ObjectRegistry.getConfig(objectName);\n const cliConfig = config?.cli;\n\n if (cliConfig === false) {\n throw new Error(`CLI is disabled for ${objectName}`);\n }\n\n const included: string[] | undefined =\n typeof cliConfig === 'object' ? cliConfig?.include : undefined;\n const excluded: string[] =\n typeof cliConfig === 'object' && cliConfig?.exclude\n ? cliConfig.exclude\n : [];\n\n if (excluded.includes(action)) {\n throw new Error(`Command '${action}' is excluded for ${objectName}`);\n }\n\n const isCrud = CRUD_OPERATIONS.includes(action);\n\n if (isCrud) {\n // An include list, when present, is the complete allowlist for CRUD too.\n if (included && !included.includes(action)) {\n throw new Error(`Command '${action}' is not enabled for ${objectName}`);\n }\n return;\n }\n\n // Custom method: must be public, and — when an include list is present — it\n // is the COMPLETE allowlist (parity with MCP exhaustive-include, #1547).\n const methods = await ObjectRegistry.getAllMethods(objectName);\n const methodDef = methods.get(action);\n if (!methodDef) {\n throw new Error(`Unknown command '${action}' for ${objectName}`);\n }\n if (!methodDef.isPublic) {\n throw new Error(`Command '${action}' is not public on ${objectName}`);\n }\n if (included !== undefined && !included.includes(action)) {\n throw new Error(`Command '${action}' is not enabled for ${objectName}`);\n }\n }\n\n /**\n * Get the collection for an object via the registry factory. Uses\n * `ObjectRegistry.getCollection`, which caches, initializes, and — for plain\n * `@smrt()` models without a hand-written collection class — auto-creates the\n * default collection. (A bare `collectionConstructor` check would wrongly\n * throw for those models even though CRUD is advertised.)\n */\n private async getCollection(\n objectName: string,\n ): Promise<SmrtCollection<any>> {\n return ObjectRegistry.getCollection(objectName, {\n ai: this.context.ai,\n db: this.context.db,\n });\n }\n\n /**\n * Execute a parsed command against a collection.\n */\n private async executeAction(\n collection: SmrtCollection<any>,\n objectName: string,\n parsed: ParsedInvocation,\n ): Promise<any> {\n const { action, positional, flags } = parsed;\n\n switch (action) {\n case 'list': {\n const listOptions: any = {\n limit: Math.min(this.toNumber(flags.limit, 50), 1000),\n offset: this.toNumber(flags.offset, 0),\n };\n const orderBy = flags['order-by'] ?? flags.orderBy;\n if (typeof orderBy === 'string') listOptions.orderBy = orderBy;\n if (typeof flags.where === 'string') {\n listOptions.where = JSON.parse(flags.where);\n }\n return collection.list(listOptions);\n }\n\n case 'get': {\n const id = positional ?? this.flagString(flags.id);\n if (!id) throw new Error('An id is required for get');\n const item = await collection.get(id);\n if (!item) throw new Error(`${objectName} not found`);\n return item;\n }\n\n case 'create': {\n const data = this.applyWritablePolicy(\n objectName,\n await this.readPayload(flags),\n );\n if (this.context.user) {\n (data as any).created_by = this.context.user.id;\n (data as any).owner_id = this.context.user.id;\n }\n const created = await collection.create(data);\n await created.save();\n return created;\n }\n\n case 'update': {\n const id = positional ?? this.flagString(flags.id);\n if (!id) throw new Error('An id is required for update');\n const existing = await collection.get(id);\n if (!existing) throw new Error(`${objectName} not found`);\n const data = this.applyWritablePolicy(\n objectName,\n await this.readPayload(flags),\n );\n Object.assign(existing, data);\n if (this.context.user) {\n (existing as any).updated_by = this.context.user.id;\n }\n await existing.save();\n return existing;\n }\n\n case 'delete': {\n const id = positional ?? this.flagString(flags.id);\n if (!id) throw new Error('An id is required for delete');\n const existing = await collection.get(id);\n if (!existing) throw new Error(`${objectName} not found`);\n await existing.delete();\n return { success: true, id, message: `${objectName} deleted` };\n }\n\n default:\n return this.executeCustomAction(collection, action, positional, flags);\n }\n }\n\n /**\n * Execute a custom method on an instance (when an id is given) or on the\n * collection (singleton actions). Mirrors the MCP custom-action path.\n */\n private async executeCustomAction(\n collection: SmrtCollection<any>,\n action: string,\n positional: string | undefined,\n flags: Record<string, string | boolean>,\n ): Promise<any> {\n const options = this.customOptions(flags);\n const id = positional ?? this.flagString(flags.id);\n\n if (id) {\n const object = (await collection.get(id)) as any;\n if (!object) throw new Error('Object not found');\n if (typeof object[action] !== 'function') {\n throw new Error(`Method '${action}' not found on object instance`);\n }\n return object[action](options);\n }\n\n if (typeof (collection as any)[action] === 'function') {\n return (collection as any)[action](options);\n }\n throw new Error(\n `Method '${action}' not found on collection. Provide an id for object-specific actions.`,\n );\n }\n\n /**\n * Resolve the create/update payload: `--from-file <path>` (JSON) takes\n * precedence; otherwise non-reserved `--field value` flags form the body.\n * Reading from a file also keeps secrets off the process argv (#1556).\n */\n private async readPayload(\n flags: Record<string, string | boolean>,\n ): Promise<Record<string, any>> {\n const fromFile = flags['from-file'];\n if (typeof fromFile === 'string' && fromFile) {\n const { readFile } = await import('node:fs/promises');\n const content = await readFile(fromFile, 'utf-8');\n const parsed = JSON.parse(content);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('--from-file must contain a JSON object');\n }\n return parsed as Record<string, any>;\n }\n\n const data: Record<string, any> = {};\n for (const [key, value] of Object.entries(flags)) {\n if (RESERVED_FLAGS.has(key)) continue;\n data[key] = this.coerceValue(value);\n }\n return data;\n }\n\n /** Collect non-reserved flags as custom-action options. */\n private customOptions(\n flags: Record<string, string | boolean>,\n ): Record<string, any> {\n const options: Record<string, any> = {};\n for (const [key, value] of Object.entries(flags)) {\n if (RESERVED_FLAGS.has(key)) continue;\n options[key] = this.coerceValue(value);\n }\n return options;\n }\n\n /**\n * Mass-assignment guard (#1540/#1556): strip framework/server-managed and\n * `@field({ readonly })` fields, and — when an `@smrt({ api: { writable } })`\n * allowlist is set — intersect with it. Identical policy to the REST/MCP\n * generators so a CLI create/update cannot set fields the API forbids.\n */\n private applyWritablePolicy(\n objectName: string | undefined,\n data: any,\n ): Record<string, any> {\n if (!data || typeof data !== 'object') return {};\n\n const serverManaged = new Set([\n 'id',\n 'tenantId',\n 'tenant_id',\n 'createdAt',\n 'created_at',\n 'updatedAt',\n 'updated_at',\n ]);\n\n const readonly = new Set<string>();\n let writable: string[] | null = null;\n\n if (objectName) {\n const apiConfig = ObjectRegistry.getConfig(objectName)?.api;\n if (\n apiConfig &&\n typeof apiConfig === 'object' &&\n Array.isArray((apiConfig as { writable?: unknown }).writable)\n ) {\n writable = (apiConfig as { writable: string[] }).writable;\n }\n\n for (const [name, def] of ObjectRegistry.getFields(objectName)) {\n if (def && (def.readonly === true || def._meta?.readonly === true)) {\n readonly.add(name);\n }\n }\n }\n\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key.startsWith('_')) continue;\n if (serverManaged.has(key)) continue;\n if (readonly.has(key)) continue;\n if (writable && !writable.includes(key)) continue;\n result[key] = value;\n }\n return result;\n }\n\n /**\n * Serialize command output, excluding `@field({ sensitive })` fields (#1540).\n * Recurses arrays/plain objects so a SmrtObject nested in a custom-action\n * result is stripped too; a cycle guard prevents infinite loops.\n */\n private toPublicData(value: any, seen: WeakSet<object> = new WeakSet()): any {\n if (value === null || typeof value !== 'object') return value;\n if (typeof value.toPublicJSON === 'function') return value.toPublicJSON();\n if (Array.isArray(value)) {\n if (seen.has(value)) return value;\n seen.add(value);\n return value.map((entry) => this.toPublicData(entry, seen));\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n if (seen.has(value)) return value;\n seen.add(value);\n const out: Record<string, any> = {};\n for (const [key, entry] of Object.entries(value)) {\n out[key] = this.toPublicData(entry, seen);\n }\n return out;\n }\n\n /** List the runnable commands grouped by object, for help output. */\n async listCommands(): Promise<string[]> {\n const commands: string[] = [];\n for (const [key, classInfo] of ObjectRegistry.getAllClasses()) {\n const objectName = classInfo.name || key;\n const config = ObjectRegistry.getConfig(objectName);\n const cliConfig = config?.cli;\n if (cliConfig === false) continue;\n\n const lower = objectName.toLowerCase();\n const included: string[] | undefined =\n typeof cliConfig === 'object' ? cliConfig?.include : undefined;\n const excluded: string[] =\n typeof cliConfig === 'object' && cliConfig?.exclude\n ? cliConfig.exclude\n : [];\n\n const expose = (cmd: string) =>\n !excluded.includes(cmd) && (!included || included.includes(cmd));\n\n for (const verb of CRUD_OPERATIONS) {\n if (expose(verb)) commands.push(`${lower}:${verb}`);\n }\n\n const methods = await ObjectRegistry.getAllMethods(objectName);\n for (const [methodName, methodDef] of methods) {\n if (CRUD_OPERATIONS.includes(methodName)) continue;\n if (!methodDef.isPublic) continue;\n if (excluded.includes(methodName)) continue;\n if (included !== undefined && !included.includes(methodName)) continue;\n commands.push(`${lower}:${methodName}`);\n }\n }\n return commands.sort();\n }\n\n private async helpText(): Promise<string> {\n const commands = await this.listCommands();\n return [\n `${this.config.name} ${this.config.version}`,\n this.config.description ?? '',\n '',\n 'Usage: <object>:<action> [id] [--flags]',\n '',\n 'Global flags:',\n ' --tenant <id> run the command inside a specific tenant',\n ' --all-tenants allow cross-tenant access (operator opt-in)',\n ' --from-file <p> read create/update payload from a JSON file',\n ' --where <json> filter for list',\n ' --limit / --offset / --order-by',\n '',\n 'Commands:',\n ...commands.map((c) => ` ${c}`),\n ].join('\\n');\n }\n\n private toNumber(\n value: string | boolean | undefined,\n fallback: number,\n ): number {\n if (typeof value !== 'string') return fallback;\n const n = Number.parseInt(value, 10);\n return Number.isNaN(n) ? fallback : n;\n }\n\n private flagString(value: string | boolean | undefined): string | undefined {\n return typeof value === 'string' ? value : undefined;\n }\n\n /** Coerce a flag value to a JSON scalar/object where it parses cleanly. */\n private coerceValue(value: string | boolean): any {\n if (typeof value !== 'string') return value;\n if (value === 'true') return true;\n if (value === 'false') return false;\n if (value !== '' && !Number.isNaN(Number(value))) return Number(value);\n if (\n (value.startsWith('{') && value.endsWith('}')) ||\n (value.startsWith('[') && value.endsWith(']'))\n ) {\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n }\n return value;\n }\n}\n\n/**\n * Convenience runner used by the generated `setupCLI()` wrapper.\n *\n * @param config - CLI configuration.\n * @param context - Per-invocation context.\n * @returns A `{ run, generator }` pair; `run(argv)` strips the leading\n * `node script` entries before dispatching.\n */\nexport function setupCLI(config: CLIConfig = {}, context: CLIContext = {}) {\n const generator = new CLIGenerator(config, context);\n return {\n run: async (argv: string[]) => {\n const handler = generator.generateHandler();\n await handler(argv.slice(2));\n },\n generator,\n };\n}\n\n/**\n * Get a bare argv handler for a generated CLI.\n *\n * @param config - CLI configuration.\n * @param context - Per-invocation context.\n * @returns An async argv-slice handler.\n */\nexport function getCLIHandler(\n config: CLIConfig = {},\n context: CLIContext = {},\n) {\n const generator = new CLIGenerator(config, context);\n return generator.generateHandler();\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../../src/generators/cli.ts"],"sourcesContent":["/**\n * CLI command generator for smrt objects\n *\n * Exposes registered `@smrt()` objects as a runnable admin CLI: each object gets\n * `list`/`get`/`create`/`update`/`delete` commands plus its public custom\n * methods, dispatched as `objectname:action`.\n *\n * Security parity with the REST/MCP generators (#1540, #1547, #1554, #1556):\n * - **Mass-assignment guard** — create/update bodies are filtered through the\n * `@smrt({ api: { writable: [...] } })` allowlist, dropping server-managed and\n * `@field({ readonly })` fields ({@link CLIGenerator.applyWritablePolicy}).\n * - **Exhaustive include** — an `include` list is the COMPLETE allowlist for the\n * surface; custom methods are gated on `isPublic`.\n * - **Sensitive redaction** — command *output* is serialized through\n * {@link CLIGenerator.toPublicData} so `@field({ sensitive })` values never\n * print (input bodies are guarded by the writable allowlist above).\n * - **Fail-closed tenant context** — tenant-scoped reads/writes run inside the\n * tenancy gate; without `--tenant <id>` / `--all-tenants` (and with tenancy\n * enabled) the command throws rather than ranging across all tenants.\n */\n\nimport type { AIClient, AIClientOptions } from '@happyvertical/ai';\nimport type { SmrtCollection } from '../collection';\nimport type { DatabaseConfig } from '../database.js';\nimport type { SmrtObject } from '../object';\nimport { ObjectRegistry } from '../registry';\nimport type { RegisteredClass } from '../registry/types.js';\nimport { runWithTenantGate } from './tenant-gate.js';\n\n/**\n * Public-data view exposed by SMRT objects: `toPublicJSON()` strips\n * `@field({ sensitive })` values before serialization (#1540).\n */\ninterface PublicSerializable {\n toPublicJSON(): unknown;\n}\n\n/**\n * View of an instance/collection for dispatching custom methods by name. Each\n * key is potentially a callable taking the parsed CLI options; callers narrow\n * with `typeof === 'function'` before invoking.\n */\ntype DynamicallyCallable = Record<\n string,\n ((options: Record<string, unknown>) => unknown) | unknown\n>;\n\n/**\n * Configuration for a generated CLI.\n */\nexport interface CLIConfig {\n /** CLI program name (used in help output). */\n name?: string;\n /** CLI version. */\n version?: string;\n /** CLI description. */\n description?: string;\n}\n\n/**\n * Per-invocation context for a generated CLI.\n */\nexport interface CLIContext {\n /** Database handle passed to collections. */\n db?: DatabaseConfig;\n /** AI provider passed to collections. */\n ai?: AIClientOptions | AIClient;\n /** Authenticated operator, when the host establishes one. */\n user?: {\n id: string;\n roles?: string[];\n };\n /**\n * Default tenant for tenant-scoped commands when no `--tenant` flag is given.\n * Hosts that authenticate an operator may set this from the principal.\n */\n tenantId?: string;\n /**\n * Default cross-tenant opt-in when no `--all-tenants` flag is given. Hosts\n * may set this for trusted operator/admin shells.\n */\n allowCrossTenant?: boolean;\n}\n\n/** Standard CRUD verbs handled directly by the generator. */\nconst CRUD_OPERATIONS = ['list', 'get', 'create', 'update', 'delete'];\n\n/**\n * Flags consumed by the CLI itself — never treated as create/update field\n * values or as custom-action options.\n */\nconst RESERVED_FLAGS = new Set([\n 'tenant',\n 'all-tenants',\n 'from-file',\n 'limit',\n 'offset',\n 'order-by',\n 'orderBy',\n 'where',\n 'format',\n 'json',\n 'help',\n 'id',\n]);\n\n/** Parsed CLI invocation. */\ninterface ParsedInvocation {\n /** Object/collection segment (lowercased), e.g. `product`. */\n objectName: string;\n /** Action segment, e.g. `list` or a custom method name. */\n action: string;\n /** First positional argument after the command (typically an id). */\n positional?: string;\n /** Parsed flags. String for `--k v`/`--k=v`, `true` for bare `--flag`. */\n flags: Record<string, string | boolean>;\n}\n\n/**\n * Generate and run an admin CLI for the registered `@smrt()` objects.\n */\nexport class CLIGenerator {\n private config: CLIConfig;\n private context: CLIContext;\n\n constructor(config: CLIConfig = {}, context: CLIContext = {}) {\n this.config = {\n name: 'smrt-cli',\n version: '1.0.0',\n description: 'Auto-generated CLI from smrt objects',\n ...config,\n };\n this.context = context;\n }\n\n /** CLI program name. */\n get name(): string | undefined {\n return this.config.name;\n }\n\n /** CLI version. */\n get version(): string | undefined {\n return this.config.version;\n }\n\n /**\n * Return the argv handler invoked by the generated `setupCLI()` wrapper.\n *\n * @returns An async function accepting the post-`node script` argv slice.\n */\n generateHandler(): (args: string[]) => Promise<void> {\n return async (args: string[]) => {\n await this.run(args ?? []);\n };\n }\n\n /**\n * Parse and execute a single CLI invocation.\n *\n * @param args - argv slice (command + flags).\n */\n async run(args: string[]): Promise<void> {\n if (args.length === 0 || args[0] === '--help' || args[0] === 'help') {\n console.log(await this.helpText());\n return;\n }\n\n const parsed = this.parseArgs(args);\n if (!parsed) {\n console.log(await this.helpText());\n return;\n }\n\n const classInfo = this.resolveClass(parsed.objectName);\n if (!classInfo) {\n throw new Error(`Object type '${parsed.objectName}' not found`);\n }\n const objectName: string = classInfo.name || parsed.objectName;\n\n await this.assertCommandExposed(objectName, parsed.action);\n\n const tenantId =\n typeof parsed.flags.tenant === 'string'\n ? parsed.flags.tenant\n : this.context.tenantId;\n const allowCrossTenant =\n parsed.flags['all-tenants'] === true ||\n this.context.allowCrossTenant === true;\n\n // Tenant-scoping is resolved authoritatively inside tenancy (by class name),\n // matching the interceptor and covering both @TenantScoped and\n // @smrt({ tenantScoped }) registrations (#1554).\n const result = await runWithTenantGate(\n { className: objectName, tenantId, allowCrossTenant, surface: 'CLI' },\n async () => {\n const collection = await this.getCollection(objectName);\n return this.executeAction(collection, objectName, parsed);\n },\n );\n\n if (result !== undefined) {\n console.log(JSON.stringify(this.toPublicData(result), null, 2));\n }\n }\n\n /**\n * Parse argv into a command + flags. Supports `name:action` and\n * `name action` command forms, `--key value`, `--key=value`, and bare\n * `--flag` booleans.\n */\n private parseArgs(args: string[]): ParsedInvocation | null {\n const [command, ...rest] = args;\n if (!command || command.startsWith('-')) return null;\n\n let objectName: string;\n let action: string;\n let tail = rest;\n\n if (command.includes(':')) {\n const [obj, act] = command.split(':');\n objectName = obj.toLowerCase();\n action = act;\n } else if (rest.length > 0 && !rest[0].startsWith('-')) {\n objectName = command.toLowerCase();\n action = rest[0];\n tail = rest.slice(1);\n } else {\n // A bare object name with no action is not a runnable command.\n return null;\n }\n\n if (!objectName || !action) return null;\n\n const flags: Record<string, string | boolean> = {};\n let positional: string | undefined;\n\n for (let i = 0; i < tail.length; i++) {\n const token = tail[i];\n if (token.startsWith('--')) {\n const body = token.slice(2);\n const eq = body.indexOf('=');\n if (eq !== -1) {\n flags[body.slice(0, eq)] = body.slice(eq + 1);\n } else {\n const next = tail[i + 1];\n if (next !== undefined && !next.startsWith('--')) {\n flags[body] = next;\n i++;\n } else {\n flags[body] = true;\n }\n }\n } else if (positional === undefined) {\n positional = token;\n }\n }\n\n return { objectName, action, positional, flags };\n }\n\n /**\n * Resolve a registered class by simple name (case-insensitive), mirroring the\n * MCP generator's lookup.\n */\n private resolveClass(objectName: string): RegisteredClass | null {\n const registeredClasses = ObjectRegistry.getAllClasses();\n for (const [key, info] of registeredClasses) {\n const simpleName = info.name || key;\n if (simpleName.toLowerCase() === objectName.toLowerCase()) {\n return info;\n }\n }\n return null;\n }\n\n /**\n * Throw if `action` is not exposed for `objectName` under its `@smrt({ cli })`\n * config. `cli: false` disables the object entirely; an `include` list is the\n * complete allowlist; custom methods must be `isPublic`.\n */\n private async assertCommandExposed(\n objectName: string,\n action: string,\n ): Promise<void> {\n const config = ObjectRegistry.getConfig(objectName);\n const cliConfig = config?.cli;\n\n if (cliConfig === false) {\n throw new Error(`CLI is disabled for ${objectName}`);\n }\n\n const included: string[] | undefined =\n typeof cliConfig === 'object' ? cliConfig?.include : undefined;\n const excluded: string[] =\n typeof cliConfig === 'object' && cliConfig?.exclude\n ? cliConfig.exclude\n : [];\n\n if (excluded.includes(action)) {\n throw new Error(`Command '${action}' is excluded for ${objectName}`);\n }\n\n const isCrud = CRUD_OPERATIONS.includes(action);\n\n if (isCrud) {\n // An include list, when present, is the complete allowlist for CRUD too.\n if (included && !included.includes(action)) {\n throw new Error(`Command '${action}' is not enabled for ${objectName}`);\n }\n return;\n }\n\n // Custom method: must be public, and — when an include list is present — it\n // is the COMPLETE allowlist (parity with MCP exhaustive-include, #1547).\n const methods = await ObjectRegistry.getAllMethods(objectName);\n const methodDef = methods.get(action);\n if (!methodDef) {\n throw new Error(`Unknown command '${action}' for ${objectName}`);\n }\n if (!methodDef.isPublic) {\n throw new Error(`Command '${action}' is not public on ${objectName}`);\n }\n if (included !== undefined && !included.includes(action)) {\n throw new Error(`Command '${action}' is not enabled for ${objectName}`);\n }\n }\n\n /**\n * Get the collection for an object via the registry factory. Uses\n * `ObjectRegistry.getCollection`, which caches, initializes, and — for plain\n * `@smrt()` models without a hand-written collection class — auto-creates the\n * default collection. (A bare `collectionConstructor` check would wrongly\n * throw for those models even though CRUD is advertised.)\n */\n private async getCollection(\n objectName: string,\n ): Promise<SmrtCollection<SmrtObject>> {\n return ObjectRegistry.getCollection(objectName, {\n ai: this.context.ai,\n db: this.context.db,\n });\n }\n\n /**\n * Execute a parsed command against a collection.\n */\n private async executeAction(\n collection: SmrtCollection<SmrtObject>,\n objectName: string,\n parsed: ParsedInvocation,\n ): Promise<unknown> {\n const { action, positional, flags } = parsed;\n\n switch (action) {\n case 'list': {\n const listOptions: {\n limit: number;\n offset: number;\n orderBy?: string;\n where?: Record<string, unknown>;\n } = {\n limit: Math.min(this.toNumber(flags.limit, 50), 1000),\n offset: this.toNumber(flags.offset, 0),\n };\n const orderBy = flags['order-by'] ?? flags.orderBy;\n if (typeof orderBy === 'string') listOptions.orderBy = orderBy;\n if (typeof flags.where === 'string') {\n listOptions.where = JSON.parse(flags.where);\n }\n return collection.list(listOptions);\n }\n\n case 'get': {\n const id = positional ?? this.flagString(flags.id);\n if (!id) throw new Error('An id is required for get');\n const item = await collection.get(id);\n if (!item) throw new Error(`${objectName} not found`);\n return item;\n }\n\n case 'create': {\n const data = this.applyWritablePolicy(\n objectName,\n await this.readPayload(flags),\n );\n if (this.context.user) {\n data.created_by = this.context.user.id;\n data.owner_id = this.context.user.id;\n }\n const created = await collection.create(data);\n await created.save();\n return created;\n }\n\n case 'update': {\n const id = positional ?? this.flagString(flags.id);\n if (!id) throw new Error('An id is required for update');\n const existing = await collection.get(id);\n if (!existing) throw new Error(`${objectName} not found`);\n const data = this.applyWritablePolicy(\n objectName,\n await this.readPayload(flags),\n );\n Object.assign(existing, data);\n if (this.context.user) {\n // Stamp the server-managed audit column on the instance; this field\n // is not part of the public SmrtObject surface.\n (existing as unknown as Record<string, unknown>).updated_by =\n this.context.user.id;\n }\n await existing.save();\n return existing;\n }\n\n case 'delete': {\n const id = positional ?? this.flagString(flags.id);\n if (!id) throw new Error('An id is required for delete');\n const existing = await collection.get(id);\n if (!existing) throw new Error(`${objectName} not found`);\n await existing.delete();\n return { success: true, id, message: `${objectName} deleted` };\n }\n\n default:\n return this.executeCustomAction(collection, action, positional, flags);\n }\n }\n\n /**\n * Execute a custom method on an instance (when an id is given) or on the\n * collection (singleton actions). Mirrors the MCP custom-action path.\n */\n private async executeCustomAction(\n collection: SmrtCollection<SmrtObject>,\n action: string,\n positional: string | undefined,\n flags: Record<string, string | boolean>,\n ): Promise<unknown> {\n const options = this.customOptions(flags);\n const id = positional ?? this.flagString(flags.id);\n\n if (id) {\n const object = await collection.get(id);\n if (!object) throw new Error('Object not found');\n // Custom methods are dispatched dynamically by name; index through a\n // callable-keyed view of the instance, narrowing before invoking.\n const candidate = (object as unknown as DynamicallyCallable)[action];\n if (typeof candidate !== 'function') {\n throw new Error(`Method '${action}' not found on object instance`);\n }\n return candidate.call(object, options);\n }\n\n const collectionCandidate = (collection as unknown as DynamicallyCallable)[\n action\n ];\n if (typeof collectionCandidate === 'function') {\n return collectionCandidate.call(collection, options);\n }\n throw new Error(\n `Method '${action}' not found on collection. Provide an id for object-specific actions.`,\n );\n }\n\n /**\n * Resolve the create/update payload: `--from-file <path>` (JSON) takes\n * precedence; otherwise non-reserved `--field value` flags form the body.\n * Reading from a file also keeps secrets off the process argv (#1556).\n */\n private async readPayload(\n flags: Record<string, string | boolean>,\n ): Promise<Record<string, unknown>> {\n const fromFile = flags['from-file'];\n if (typeof fromFile === 'string' && fromFile) {\n const { readFile } = await import('node:fs/promises');\n const content = await readFile(fromFile, 'utf-8');\n const parsed = JSON.parse(content);\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new Error('--from-file must contain a JSON object');\n }\n return parsed as Record<string, unknown>;\n }\n\n const data: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(flags)) {\n if (RESERVED_FLAGS.has(key)) continue;\n data[key] = this.coerceValue(value);\n }\n return data;\n }\n\n /** Collect non-reserved flags as custom-action options. */\n private customOptions(\n flags: Record<string, string | boolean>,\n ): Record<string, unknown> {\n const options: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(flags)) {\n if (RESERVED_FLAGS.has(key)) continue;\n options[key] = this.coerceValue(value);\n }\n return options;\n }\n\n /**\n * Mass-assignment guard (#1540/#1556): strip framework/server-managed and\n * `@field({ readonly })` fields, and — when an `@smrt({ api: { writable } })`\n * allowlist is set — intersect with it. Identical policy to the REST/MCP\n * generators so a CLI create/update cannot set fields the API forbids.\n */\n private applyWritablePolicy(\n objectName: string | undefined,\n data: unknown,\n ): Record<string, unknown> {\n if (!data || typeof data !== 'object') return {};\n\n const serverManaged = new Set([\n 'id',\n 'tenantId',\n 'tenant_id',\n 'createdAt',\n 'created_at',\n 'updatedAt',\n 'updated_at',\n ]);\n\n const readonly = new Set<string>();\n let writable: string[] | null = null;\n\n if (objectName) {\n const apiConfig = ObjectRegistry.getConfig(objectName)?.api;\n if (\n apiConfig &&\n typeof apiConfig === 'object' &&\n Array.isArray((apiConfig as { writable?: unknown }).writable)\n ) {\n writable = (apiConfig as { writable: string[] }).writable;\n }\n\n for (const [name, def] of ObjectRegistry.getFields(objectName)) {\n if (def && (def.readonly === true || def._meta?.readonly === true)) {\n readonly.add(name);\n }\n }\n }\n\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key.startsWith('_')) continue;\n if (serverManaged.has(key)) continue;\n if (readonly.has(key)) continue;\n if (writable && !writable.includes(key)) continue;\n result[key] = value;\n }\n return result;\n }\n\n /**\n * Serialize command output, excluding `@field({ sensitive })` fields (#1540).\n * Recurses arrays/plain objects so a SmrtObject nested in a custom-action\n * result is stripped too; a cycle guard prevents infinite loops.\n */\n private toPublicData(\n value: unknown,\n seen: WeakSet<object> = new WeakSet(),\n ): unknown {\n if (value === null || typeof value !== 'object') return value;\n if (\n typeof (value as Partial<PublicSerializable>).toPublicJSON === 'function'\n ) {\n return (value as PublicSerializable).toPublicJSON();\n }\n if (Array.isArray(value)) {\n if (seen.has(value)) return value;\n seen.add(value);\n return value.map((entry) => this.toPublicData(entry, seen));\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n if (seen.has(value)) return value;\n seen.add(value);\n const out: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(value)) {\n out[key] = this.toPublicData(entry, seen);\n }\n return out;\n }\n\n /** List the runnable commands grouped by object, for help output. */\n async listCommands(): Promise<string[]> {\n const commands: string[] = [];\n for (const [key, classInfo] of ObjectRegistry.getAllClasses()) {\n const objectName = classInfo.name || key;\n const config = ObjectRegistry.getConfig(objectName);\n const cliConfig = config?.cli;\n if (cliConfig === false) continue;\n\n const lower = objectName.toLowerCase();\n const included: string[] | undefined =\n typeof cliConfig === 'object' ? cliConfig?.include : undefined;\n const excluded: string[] =\n typeof cliConfig === 'object' && cliConfig?.exclude\n ? cliConfig.exclude\n : [];\n\n const expose = (cmd: string) =>\n !excluded.includes(cmd) && (!included || included.includes(cmd));\n\n for (const verb of CRUD_OPERATIONS) {\n if (expose(verb)) commands.push(`${lower}:${verb}`);\n }\n\n const methods = await ObjectRegistry.getAllMethods(objectName);\n for (const [methodName, methodDef] of methods) {\n if (CRUD_OPERATIONS.includes(methodName)) continue;\n if (!methodDef.isPublic) continue;\n if (excluded.includes(methodName)) continue;\n if (included !== undefined && !included.includes(methodName)) continue;\n commands.push(`${lower}:${methodName}`);\n }\n }\n return commands.sort();\n }\n\n private async helpText(): Promise<string> {\n const commands = await this.listCommands();\n return [\n `${this.config.name} ${this.config.version}`,\n this.config.description ?? '',\n '',\n 'Usage: <object>:<action> [id] [--flags]',\n '',\n 'Global flags:',\n ' --tenant <id> run the command inside a specific tenant',\n ' --all-tenants allow cross-tenant access (operator opt-in)',\n ' --from-file <p> read create/update payload from a JSON file',\n ' --where <json> filter for list',\n ' --limit / --offset / --order-by',\n '',\n 'Commands:',\n ...commands.map((c) => ` ${c}`),\n ].join('\\n');\n }\n\n private toNumber(\n value: string | boolean | undefined,\n fallback: number,\n ): number {\n if (typeof value !== 'string') return fallback;\n const n = Number.parseInt(value, 10);\n return Number.isNaN(n) ? fallback : n;\n }\n\n private flagString(value: string | boolean | undefined): string | undefined {\n return typeof value === 'string' ? value : undefined;\n }\n\n /** Coerce a flag value to a JSON scalar/object where it parses cleanly. */\n private coerceValue(value: string | boolean): unknown {\n if (typeof value !== 'string') return value;\n if (value === 'true') return true;\n if (value === 'false') return false;\n if (value !== '' && !Number.isNaN(Number(value))) return Number(value);\n if (\n (value.startsWith('{') && value.endsWith('}')) ||\n (value.startsWith('[') && value.endsWith(']'))\n ) {\n try {\n return JSON.parse(value);\n } catch {\n return value;\n }\n }\n return value;\n }\n}\n\n/**\n * Convenience runner used by the generated `setupCLI()` wrapper.\n *\n * @param config - CLI configuration.\n * @param context - Per-invocation context.\n * @returns A `{ run, generator }` pair; `run(argv)` strips the leading\n * `node script` entries before dispatching.\n */\nexport function setupCLI(config: CLIConfig = {}, context: CLIContext = {}) {\n const generator = new CLIGenerator(config, context);\n return {\n run: async (argv: string[]) => {\n const handler = generator.generateHandler();\n await handler(argv.slice(2));\n },\n generator,\n };\n}\n\n/**\n * Get a bare argv handler for a generated CLI.\n *\n * @param config - CLI configuration.\n * @param context - Per-invocation context.\n * @returns An async argv-slice handler.\n */\nexport function getCLIHandler(\n config: CLIConfig = {},\n context: CLIContext = {},\n) {\n const generator = new CLIGenerator(config, context);\n return generator.generateHandler();\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-runtime-template.d.ts","sourceRoot":"","sources":["../../src/generators/mcp-runtime-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAStD,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,kCAAkC;IAClC,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,UAAU,CAAC;IAErB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,wDAAwD;IACxD,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,
|
|
1
|
+
{"version":3,"file":"mcp-runtime-template.d.ts","sourceRoot":"","sources":["../../src/generators/mcp-runtime-template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAStD,MAAM,WAAW,cAAc;IAC7B,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,mDAAmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,yBAAyB;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,kCAAkC;IAClC,MAAM,CAAC,EAAE,SAAS,CAAC;IAEnB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,UAAU,CAAC;IAErB,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAEhB,wDAAwD;IACxD,KAAK,CAAC,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACtC,CAAC,CAAC;IAEH;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,GAAE,cAAmB,GAAG,MAAM,CAyZ7E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,UAAU,GAAE,MAA6B,GACxC,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,MAAM,CASR;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,GACjB,MAAM,CA0GR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-runtime-template.js","sources":["../../src/generators/mcp-runtime-template.ts"],"sourcesContent":["/**\n * Runtime bootstrap template for generated MCP servers\n *\n * This template provides stdio transport integration for SMRT-generated MCP servers.\n * It handles:\n * - Server initialization with @modelcontextprotocol/sdk\n * - Tool registration from MCPGenerator\n * - Stdio transport connection\n * - Error handling and logging\n * - Graceful shutdown\n */\n\nimport type { MCPConfig, MCPContext } from './mcp.js';\n\n/**\n * Helper function to capitalize first letter\n */\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nexport interface RuntimeOptions {\n /** Server name (defaults to package name) */\n name?: string;\n\n /** Server version (defaults to package version) */\n version?: string;\n\n /** Server description */\n description?: string;\n\n /** MCP generator configuration */\n config?: MCPConfig;\n\n /** MCP context (database, AI client, etc.) */\n context?: MCPContext;\n\n /** Enable debug logging */\n debug?: boolean;\n\n /** Static tool definitions (generated at build time) */\n tools?: Array<{\n name: string;\n description: string;\n inputSchema: any;\n }>;\n\n /**\n * Lowercased simple names of objects that are `@TenantScoped` (#1554). When\n * non-empty, the generated server imports the tenancy fail-closed gate and\n * wraps tenant-scoped tool calls so a stdio invocation cannot read across all\n * tenants. The tenant is sourced from `SMRT_MCP_TENANT_ID` /\n * `SMRT_MCP_ALLOW_CROSS_TENANT` env vars (this server has no auth principal).\n */\n tenantScopedObjects?: string[];\n}\n\n/**\n * Generate runtime bootstrap code for MCP server\n *\n * @param options - Runtime configuration options\n * @returns TypeScript code for server entry point\n */\nexport function generateRuntimeBootstrap(options: RuntimeOptions = {}): string {\n const {\n name = 'smrt-mcp-server',\n version = '1.0.0',\n description = 'Auto-generated MCP server from SMRT objects',\n debug = false,\n tools = [],\n tenantScopedObjects = [],\n } = options;\n\n // Generate static tool array as TypeScript code\n const toolsCode = tools.length > 0 ? JSON.stringify(tools, null, 2) : '[]';\n\n // Fail-closed tenant context (#1554): only wire the tenancy gate when at\n // least one exposed object is tenant-scoped, so apps without tenancy never\n // get a dangling import.\n const tenantScopedSet = Array.from(\n new Set(tenantScopedObjects.map((n) => n.toLowerCase())),\n );\n const hasTenantScoped = tenantScopedSet.length > 0;\n\n // Generate static switch cases using shared helper\n const generateSwitchCases = (indent: string) => {\n return tools\n .map((tool) => {\n const [objectName, action] = tool.name.split('_');\n\n switch (action) {\n case 'list':\n return `${indent}case '${tool.name}': {\n${indent} const limit = args.limit ?? 50;\n${indent} const offset = args.offset ?? 0;\n${indent} const where = args.where ?? {};\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const items = await collection.list({ where, limit, offset });\n${indent} const itemsPublic = items.map((item) => item.toPublicJSON());\n${indent} return { content: [{ type: 'text', text: JSON.stringify(itemsPublic) }] };\n${indent}}`;\n\n case 'get':\n return `${indent}case '${tool.name}': {\n${indent} if (!args.id && !args.slug) {\n${indent} throw new Error('Either id or slug is required');\n${indent} }\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const filter = args.id || args.slug;\n${indent} const item = await collection.get(filter);\n\n${indent} if (!item) {\n${indent} throw new Error('Object not found');\n${indent} }\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify(item.toPublicJSON()) }] };\n${indent}}`;\n\n case 'create':\n return `${indent}case '${tool.name}': {\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const newItem = await collection.create(applyWritablePolicy('${capitalize(objectName)}', args));\n${indent} await newItem.save();\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify(newItem.toPublicJSON()) }] };\n${indent}}`;\n\n case 'update':\n return `${indent}case '${tool.name}': {\n${indent} const { id, ...updateData } = args;\n${indent} if (!id) {\n${indent} throw new Error('ID is required for update');\n${indent} }\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const existing = await collection.get(id);\n${indent} if (!existing) {\n${indent} throw new Error('Object not found');\n${indent} }\n\n${indent} Object.assign(existing, applyWritablePolicy('${capitalize(objectName)}', updateData));\n${indent} await existing.save();\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify(existing.toPublicJSON()) }] };\n${indent}}`;\n\n case 'delete':\n return `${indent}case '${tool.name}': {\n${indent} if (!args.id) {\n${indent} throw new Error('ID is required for delete');\n${indent} }\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const toDelete = await collection.get(args.id);\n${indent} if (!toDelete) {\n${indent} throw new Error('Object not found');\n${indent} }\n\n${indent} await toDelete.delete();\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify({ success: true, message: 'Object deleted successfully' }) }] };\n${indent}}`;\n\n default:\n // Custom action\n return `${indent}case '${tool.name}': {\n${indent} const { id, options = {}, ...directArgs } = args;\n\n${indent} if (!id) {\n${indent} throw new Error('ID is required for custom action ${action}');\n${indent} }\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const object = await collection.get(id);\n${indent} if (!object) {\n${indent} throw new Error('Object not found');\n${indent} }\n\n${indent} if (typeof object['${action}'] !== 'function') {\n${indent} throw new Error('Method ${action} not found on object');\n${indent} }\n\n${indent} const methodArgs = Object.keys(options).length > 0 ? options : directArgs;\n${indent} const result = await object['${action}'](methodArgs);\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify(toPublicResult(result)) }] };\n${indent}}`;\n }\n })\n .join('\\n\\n');\n };\n\n const switchCases = generateSwitchCases(' ');\n\n return `#!/usr/bin/env node\n/**\n * Auto-generated MCP Server\n * Generated by @smrt/core MCPGenerator\n *\n * This server exposes SMRT objects as MCP tools for AI integration.\n *\n * SECURITY (#1540): tool responses exclude @field({ sensitive }) fields and\n * create/update bodies are mass-assignment guarded. This stdio server has NO\n * per-call authentication principal — its trust boundary is the host process /\n * MCP client that launches it. Run it only in a trusted context, or front it\n * with an authenticated gateway. Do not expose it directly to untrusted callers.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type CallToolRequest,\n type ListToolsRequest,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\nimport { config } from '@happyvertical/smrt-config';\n${hasTenantScoped ? \"import { enableTenancy, runTenantScopedEntryPoint } from '@happyvertical/smrt-tenancy';\\n\" : ''}\n// Server configuration\nconst SERVER_NAME = ${JSON.stringify(name)};\nconst SERVER_VERSION = ${JSON.stringify(version)};\nconst SERVER_DESCRIPTION = ${JSON.stringify(description)};\nconst DEBUG = ${debug};\n\n// Static tool definitions (generated at build time)\nconst TOOLS = ${toolsCode};\n${\n hasTenantScoped\n ? `\n// Fail-closed tenant context (#1554): tenant-scoped objects must run inside a\n// tenant. This stdio server has no auth principal, so the tenant is taken from\n// the environment; without it (and with tenancy enabled) tenant-scoped tools\n// throw rather than reading across all tenants.\nconst TENANT_SCOPED = new Set(${JSON.stringify(tenantScopedSet)});\nconst MCP_TENANT_ID = process.env.SMRT_MCP_TENANT_ID || undefined;\nconst MCP_ALLOW_CROSS_TENANT = process.env.SMRT_MCP_ALLOW_CROSS_TENANT === 'true';\n`\n : ''\n}\n\n/**\n * Mass-assignment guard (#1540): strip framework/server-managed and\n * \\`@field({ readonly: true })\\` fields from create/update bodies, intersecting\n * with the optional \\`@smrt({ api: { writable: [...] } })\\` allowlist.\n */\nfunction applyWritablePolicy(objectName: string, data: any): Record<string, any> {\n if (!data || typeof data !== 'object') return {};\n const serverManaged = new Set([\n 'id', 'tenantId', 'tenant_id',\n 'createdAt', 'created_at', 'updatedAt', 'updated_at',\n ]);\n const readonly = new Set<string>();\n let writable: string[] | null = null;\n const apiConfig = ObjectRegistry.getConfig(objectName)?.api as any;\n if (apiConfig && typeof apiConfig === 'object' && Array.isArray(apiConfig.writable)) {\n writable = apiConfig.writable;\n }\n for (const [name, def] of ObjectRegistry.getFields(objectName)) {\n if (def && ((def as any).readonly === true || (def as any)._meta?.readonly === true)) {\n readonly.add(name);\n }\n }\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key.startsWith('_')) continue;\n if (serverManaged.has(key)) continue;\n if (readonly.has(key)) continue;\n if (writable && !writable.includes(key)) continue;\n result[key] = value;\n }\n return result;\n}\n\n/**\n * Sensitive-field-safe serialization for custom-action results (#1540).\n * Recurses through arrays and plain objects so nested SmrtObjects are stripped\n * too; non-plain instances (Date, etc.) and primitives pass through. Cycle-safe.\n */\nfunction toPublicResult(value: any, seen: WeakSet<object> = new WeakSet()): any {\n if (value === null || typeof value !== 'object') return value;\n if (typeof value.toPublicJSON === 'function') return value.toPublicJSON();\n if (Array.isArray(value)) {\n if (seen.has(value)) return value;\n seen.add(value);\n return value.map((entry: any) => toPublicResult(entry, seen));\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n if (seen.has(value)) return value;\n seen.add(value);\n const out: Record<string, any> = {};\n for (const [key, entry] of Object.entries(value)) {\n out[key] = toPublicResult(entry, seen);\n }\n return out;\n}\n\n/**\n * Main server startup function\n */\nasync function main() {\n try {\n if (DEBUG) {\n console.error(\\`[MCP] Starting server: \\${SERVER_NAME} v\\${SERVER_VERSION}\\`);\n }\n${\n hasTenantScoped\n ? `\n // Fail-closed tenant context (#1554): install the tenancy interceptor so\n // tenant-scoped tools are actually filtered, and so the entry-point gate\n // throws (rather than passing through) when no tenant is supplied. Without\n // this, a tenant set via SMRT_MCP_TENANT_ID would only set async context\n // with no interceptor to enforce it.\n enableTenancy();\n`\n : ''\n}\n // Load configuration from environment and .smrt.config files\n const appConfig = await config.load();\n const aiConfig = appConfig?.ai || {};\n\n if (DEBUG) {\n console.error(\\`[MCP] Loaded \\${TOOLS.length} static tools\\`);\n console.error(\\`[MCP] Available tools:\\`, TOOLS.map(t => t.name).join(', '));\n }\n\n // Create MCP server\n const server = new Server(\n {\n name: SERVER_NAME,\n version: SERVER_VERSION,\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n // Register ListTools handler\n server.setRequestHandler(ListToolsRequestSchema, async (_request: ListToolsRequest) => {\n if (DEBUG) {\n console.error(\\`[MCP] ListTools request received\\`);\n }\n\n return {\n tools: TOOLS,\n };\n });\n\n // Register CallTool handler\n server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n const { name: toolName, arguments: args = {} } = request.params;\n\n if (DEBUG) {\n console.error(\\`[MCP] CallTool request: \\${toolName}\\`);\n console.error(\\`[MCP] Arguments:\\`, JSON.stringify(args, null, 2));\n }\n\n try {\n // Static switch statement for tool execution\n const runToolBody = async () => {\n switch (toolName) {\n${switchCases}\n\n default:\n throw new Error(\\`Unknown tool: \\${toolName}\\`);\n }\n };\n${\n hasTenantScoped\n ? `\n // Fail-closed tenant context for tenant-scoped tools (#1554).\n const [toolObject] = toolName.split('_');\n const result =\n toolObject && TENANT_SCOPED.has(toolObject.toLowerCase())\n ? await runTenantScopedEntryPoint(\n { tenantScoped: true, tenantId: MCP_TENANT_ID, allowCrossTenant: MCP_ALLOW_CROSS_TENANT, surface: 'MCP' },\n runToolBody,\n )\n : await runToolBody();`\n : `\n const result = await runToolBody();`\n}\n\n if (DEBUG) {\n console.error(\\`[MCP] Tool executed successfully: \\${toolName}\\`);\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n console.error(\\`[MCP] Tool execution failed: \\${toolName}\\`, error);\n\n return {\n content: [\n {\n type: 'text',\n text: \\`Error executing tool \\${toolName}: \\${errorMessage}\\`,\n },\n ],\n isError: true,\n };\n }\n });\n\n // Setup stdio transport\n const transport = new StdioServerTransport();\n\n // Connect server to transport\n await server.connect(transport);\n\n if (DEBUG) {\n console.error(\\`[MCP] Server connected via stdio transport\\`);\n console.error(\\`[MCP] Ready to receive requests\\`);\n }\n\n // Handle graceful shutdown\n process.on('SIGINT', async () => {\n if (DEBUG) {\n console.error(\\`[MCP] Received SIGINT, shutting down gracefully\\`);\n }\n await server.close();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n if (DEBUG) {\n console.error(\\`[MCP] Received SIGTERM, shutting down gracefully\\`);\n }\n await server.close();\n process.exit(0);\n });\n } catch (error) {\n console.error('[MCP] Fatal error during server startup:', error);\n process.exit(1);\n }\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('[MCP] Unhandled error:', error);\n process.exit(1);\n});\n`;\n}\n\n/**\n * Generate package.json script for running MCP server\n *\n * @param serverPath - Path to generated server file (relative to package root)\n * @returns Script command for package.json\n */\nexport function generateMCPScript(\n serverPath: string = 'dist/mcp-server.js',\n): string {\n return `node ${serverPath}`;\n}\n\n/**\n * Generate Claude Desktop configuration example\n *\n * @param serverName - Name for the MCP server\n * @param serverPath - Absolute path to server file\n * @returns Configuration object for claude_desktop_config.json\n */\nexport function generateClaudeConfig(\n serverName: string,\n serverPath: string,\n): object {\n return {\n mcpServers: {\n [serverName]: {\n command: 'node',\n args: [serverPath],\n },\n },\n };\n}\n\n/**\n * Generate README documentation for MCP server setup\n *\n * @param serverName - Name of the MCP server\n * @param serverPath - Path to the server file\n * @returns Markdown documentation\n */\nexport function generateMCPDocumentation(\n serverName: string,\n serverPath: string,\n): string {\n return `# MCP Server Setup\n\nThis project includes an auto-generated MCP (Model Context Protocol) server that exposes SMRT objects as tools for AI integration.\n\n## Quick Start\n\n### 1. Build the MCP Server\n\n\\`\\`\\`bash\nnpm run build\n\\`\\`\\`\n\nThis generates the MCP server at: \\`${serverPath}\\`\n\n### 2. Configure Claude Desktop\n\nAdd the following to your Claude Desktop configuration file:\n\n**macOS**: \\`~/.config/Claude/claude_desktop_config.json\\`\n**Windows**: \\`%APPDATA%\\\\Claude\\\\claude_desktop_config.json\\`\n\n\\`\\`\\`json\n{\n \"mcpServers\": {\n \"${serverName}\": {\n \"command\": \"node\",\n \"args\": [\"/absolute/path/to/${serverPath}\"]\n }\n }\n}\n\\`\\`\\`\n\nReplace \\`/absolute/path/to/\\` with the actual absolute path to your project directory.\n\n### 3. Restart Claude Desktop\n\nClose and reopen Claude Desktop to load the new MCP server.\n\n### 4. Test the Integration\n\nIn Claude Code, you can now use the auto-generated tools. For example:\n\n- \\`list_products\\` - List all products\n- \\`get_product\\` - Get a specific product by ID\n- \\`create_product\\` - Create a new product\n- And more...\n\n## Environment Variables\n\nThe MCP server supports optional environment variables:\n\n- \\`DATABASE_URL\\` - Database connection string\n\n**AI Provider Configuration (in priority order):**\n1. **Generic configuration** (supports any provider):\n - \\`SMRT_AI_PROVIDER\\` - Provider name (e.g., 'openai', 'anthropic', 'claude-cli', 'gemini')\n - \\`SMRT_AI_API_KEY\\` - API key for the provider\n - \\`SMRT_AI_MODEL\\` - Model to use (optional)\n\n2. **Provider-specific fallbacks**:\n - \\`OPENAI_API_KEY\\` - OpenAI API key (auto-detects provider as 'openai')\n - \\`ANTHROPIC_API_KEY\\` - Anthropic API key (auto-detects provider as 'anthropic')\n - \\`CLAUDE_API_KEY\\` + \\`CLAUDE_MODEL\\` - Claude CLI provider (defaults to 'sonnet')\n\n**Examples:**\n\\`\\`\\`bash\n# Using generic configuration (recommended)\nexport SMRT_AI_PROVIDER=claude-cli\nexport SMRT_AI_MODEL=sonnet\n\n# Using provider-specific configuration\nexport CLAUDE_API_KEY=your-key\nexport CLAUDE_MODEL=sonnet\n\n# Using OpenAI\nexport OPENAI_API_KEY=your-openai-key\n\\`\\`\\`\n\n## Troubleshooting\n\n### Server Not Appearing in Claude\n\n1. Check that the path in \\`claude_desktop_config.json\\` is absolute\n2. Verify the server file exists at the specified path\n3. Check Claude Desktop logs for errors\n\n### Tools Not Working\n\n1. Ensure your database is accessible (if using one)\n2. Check that SMRT objects are properly decorated with \\`@smrt()\\`\n3. Look for errors in the MCP server output\n\n### Debug Mode\n\nTo enable debug logging, set the \\`DEBUG\\` constant to \\`true\\` in the generated server file.\n\n## Generated Tools\n\nThe following tools are automatically generated from your SMRT objects:\n\n- **CRUD Operations**: \\`list_\\`, \\`get_\\`, \\`create_\\`, \\`update_\\`, \\`delete_\\` for each object type\n- **Custom Actions**: Any custom methods included in the \\`@smrt()\\` decorator configuration\n\nSee the SMRT object definitions for the complete list of available tools and their parameters.\n`;\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp-runtime-template.js","sources":["../../src/generators/mcp-runtime-template.ts"],"sourcesContent":["/**\n * Runtime bootstrap template for generated MCP servers\n *\n * This template provides stdio transport integration for SMRT-generated MCP servers.\n * It handles:\n * - Server initialization with @modelcontextprotocol/sdk\n * - Tool registration from MCPGenerator\n * - Stdio transport connection\n * - Error handling and logging\n * - Graceful shutdown\n */\n\nimport type { MCPConfig, MCPContext } from './mcp.js';\n\n/**\n * Helper function to capitalize first letter\n */\nfunction capitalize(str: string): string {\n return str.charAt(0).toUpperCase() + str.slice(1);\n}\n\nexport interface RuntimeOptions {\n /** Server name (defaults to package name) */\n name?: string;\n\n /** Server version (defaults to package version) */\n version?: string;\n\n /** Server description */\n description?: string;\n\n /** MCP generator configuration */\n config?: MCPConfig;\n\n /** MCP context (database, AI client, etc.) */\n context?: MCPContext;\n\n /** Enable debug logging */\n debug?: boolean;\n\n /** Static tool definitions (generated at build time) */\n tools?: Array<{\n name: string;\n description: string;\n inputSchema: Record<string, unknown>;\n }>;\n\n /**\n * Lowercased simple names of objects that are `@TenantScoped` (#1554). When\n * non-empty, the generated server imports the tenancy fail-closed gate and\n * wraps tenant-scoped tool calls so a stdio invocation cannot read across all\n * tenants. The tenant is sourced from `SMRT_MCP_TENANT_ID` /\n * `SMRT_MCP_ALLOW_CROSS_TENANT` env vars (this server has no auth principal).\n */\n tenantScopedObjects?: string[];\n}\n\n/**\n * Generate runtime bootstrap code for MCP server\n *\n * @param options - Runtime configuration options\n * @returns TypeScript code for server entry point\n */\nexport function generateRuntimeBootstrap(options: RuntimeOptions = {}): string {\n const {\n name = 'smrt-mcp-server',\n version = '1.0.0',\n description = 'Auto-generated MCP server from SMRT objects',\n debug = false,\n tools = [],\n tenantScopedObjects = [],\n } = options;\n\n // Generate static tool array as TypeScript code\n const toolsCode = tools.length > 0 ? JSON.stringify(tools, null, 2) : '[]';\n\n // Fail-closed tenant context (#1554): only wire the tenancy gate when at\n // least one exposed object is tenant-scoped, so apps without tenancy never\n // get a dangling import.\n const tenantScopedSet = Array.from(\n new Set(tenantScopedObjects.map((n) => n.toLowerCase())),\n );\n const hasTenantScoped = tenantScopedSet.length > 0;\n\n // Generate static switch cases using shared helper\n const generateSwitchCases = (indent: string) => {\n return tools\n .map((tool) => {\n const [objectName, action] = tool.name.split('_');\n\n switch (action) {\n case 'list':\n return `${indent}case '${tool.name}': {\n${indent} const limit = args.limit ?? 50;\n${indent} const offset = args.offset ?? 0;\n${indent} const where = args.where ?? {};\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const items = await collection.list({ where, limit, offset });\n${indent} const itemsPublic = items.map((item) => item.toPublicJSON());\n${indent} return { content: [{ type: 'text', text: JSON.stringify(itemsPublic) }] };\n${indent}}`;\n\n case 'get':\n return `${indent}case '${tool.name}': {\n${indent} if (!args.id && !args.slug) {\n${indent} throw new Error('Either id or slug is required');\n${indent} }\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const filter = args.id || args.slug;\n${indent} const item = await collection.get(filter);\n\n${indent} if (!item) {\n${indent} throw new Error('Object not found');\n${indent} }\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify(item.toPublicJSON()) }] };\n${indent}}`;\n\n case 'create':\n return `${indent}case '${tool.name}': {\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const newItem = await collection.create(applyWritablePolicy('${capitalize(objectName)}', args));\n${indent} await newItem.save();\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify(newItem.toPublicJSON()) }] };\n${indent}}`;\n\n case 'update':\n return `${indent}case '${tool.name}': {\n${indent} const { id, ...updateData } = args;\n${indent} if (!id) {\n${indent} throw new Error('ID is required for update');\n${indent} }\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const existing = await collection.get(id);\n${indent} if (!existing) {\n${indent} throw new Error('Object not found');\n${indent} }\n\n${indent} Object.assign(existing, applyWritablePolicy('${capitalize(objectName)}', updateData));\n${indent} await existing.save();\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify(existing.toPublicJSON()) }] };\n${indent}}`;\n\n case 'delete':\n return `${indent}case '${tool.name}': {\n${indent} if (!args.id) {\n${indent} throw new Error('ID is required for delete');\n${indent} }\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const toDelete = await collection.get(args.id);\n${indent} if (!toDelete) {\n${indent} throw new Error('Object not found');\n${indent} }\n\n${indent} await toDelete.delete();\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify({ success: true, message: 'Object deleted successfully' }) }] };\n${indent}}`;\n\n default:\n // Custom action\n return `${indent}case '${tool.name}': {\n${indent} const { id, options = {}, ...directArgs } = args;\n\n${indent} if (!id) {\n${indent} throw new Error('ID is required for custom action ${action}');\n${indent} }\n\n${indent} const collection = await ObjectRegistry.getCollection('${capitalize(objectName)}', {\n${indent} persistence: { type: 'sql', url: process.env.DATABASE_URL || ':memory:' },\n${indent} ai: aiConfig\n${indent} });\n\n${indent} const object = await collection.get(id);\n${indent} if (!object) {\n${indent} throw new Error('Object not found');\n${indent} }\n\n${indent} if (typeof object['${action}'] !== 'function') {\n${indent} throw new Error('Method ${action} not found on object');\n${indent} }\n\n${indent} const methodArgs = Object.keys(options).length > 0 ? options : directArgs;\n${indent} const result = await object['${action}'](methodArgs);\n\n${indent} return { content: [{ type: 'text', text: JSON.stringify(toPublicResult(result)) }] };\n${indent}}`;\n }\n })\n .join('\\n\\n');\n };\n\n const switchCases = generateSwitchCases(' ');\n\n return `#!/usr/bin/env node\n/**\n * Auto-generated MCP Server\n * Generated by @smrt/core MCPGenerator\n *\n * This server exposes SMRT objects as MCP tools for AI integration.\n *\n * SECURITY (#1540): tool responses exclude @field({ sensitive }) fields and\n * create/update bodies are mass-assignment guarded. This stdio server has NO\n * per-call authentication principal — its trust boundary is the host process /\n * MCP client that launches it. Run it only in a trusted context, or front it\n * with an authenticated gateway. Do not expose it directly to untrusted callers.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type CallToolRequest,\n type ListToolsRequest,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\nimport { config } from '@happyvertical/smrt-config';\n${hasTenantScoped ? \"import { enableTenancy, runTenantScopedEntryPoint } from '@happyvertical/smrt-tenancy';\\n\" : ''}\n// Server configuration\nconst SERVER_NAME = ${JSON.stringify(name)};\nconst SERVER_VERSION = ${JSON.stringify(version)};\nconst SERVER_DESCRIPTION = ${JSON.stringify(description)};\nconst DEBUG = ${debug};\n\n// Static tool definitions (generated at build time)\nconst TOOLS = ${toolsCode};\n${\n hasTenantScoped\n ? `\n// Fail-closed tenant context (#1554): tenant-scoped objects must run inside a\n// tenant. This stdio server has no auth principal, so the tenant is taken from\n// the environment; without it (and with tenancy enabled) tenant-scoped tools\n// throw rather than reading across all tenants.\nconst TENANT_SCOPED = new Set(${JSON.stringify(tenantScopedSet)});\nconst MCP_TENANT_ID = process.env.SMRT_MCP_TENANT_ID || undefined;\nconst MCP_ALLOW_CROSS_TENANT = process.env.SMRT_MCP_ALLOW_CROSS_TENANT === 'true';\n`\n : ''\n}\n\n/**\n * Mass-assignment guard (#1540): strip framework/server-managed and\n * \\`@field({ readonly: true })\\` fields from create/update bodies, intersecting\n * with the optional \\`@smrt({ api: { writable: [...] } })\\` allowlist.\n */\nfunction applyWritablePolicy(objectName: string, data: any): Record<string, any> {\n if (!data || typeof data !== 'object') return {};\n const serverManaged = new Set([\n 'id', 'tenantId', 'tenant_id',\n 'createdAt', 'created_at', 'updatedAt', 'updated_at',\n ]);\n const readonly = new Set<string>();\n let writable: string[] | null = null;\n const apiConfig = ObjectRegistry.getConfig(objectName)?.api as any;\n if (apiConfig && typeof apiConfig === 'object' && Array.isArray(apiConfig.writable)) {\n writable = apiConfig.writable;\n }\n for (const [name, def] of ObjectRegistry.getFields(objectName)) {\n if (def && ((def as any).readonly === true || (def as any)._meta?.readonly === true)) {\n readonly.add(name);\n }\n }\n const result: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key.startsWith('_')) continue;\n if (serverManaged.has(key)) continue;\n if (readonly.has(key)) continue;\n if (writable && !writable.includes(key)) continue;\n result[key] = value;\n }\n return result;\n}\n\n/**\n * Sensitive-field-safe serialization for custom-action results (#1540).\n * Recurses through arrays and plain objects so nested SmrtObjects are stripped\n * too; non-plain instances (Date, etc.) and primitives pass through. Cycle-safe.\n */\nfunction toPublicResult(value: any, seen: WeakSet<object> = new WeakSet()): any {\n if (value === null || typeof value !== 'object') return value;\n if (typeof value.toPublicJSON === 'function') return value.toPublicJSON();\n if (Array.isArray(value)) {\n if (seen.has(value)) return value;\n seen.add(value);\n return value.map((entry: any) => toPublicResult(entry, seen));\n }\n const proto = Object.getPrototypeOf(value);\n if (proto !== Object.prototype && proto !== null) return value;\n if (seen.has(value)) return value;\n seen.add(value);\n const out: Record<string, any> = {};\n for (const [key, entry] of Object.entries(value)) {\n out[key] = toPublicResult(entry, seen);\n }\n return out;\n}\n\n/**\n * Main server startup function\n */\nasync function main() {\n try {\n if (DEBUG) {\n console.error(\\`[MCP] Starting server: \\${SERVER_NAME} v\\${SERVER_VERSION}\\`);\n }\n${\n hasTenantScoped\n ? `\n // Fail-closed tenant context (#1554): install the tenancy interceptor so\n // tenant-scoped tools are actually filtered, and so the entry-point gate\n // throws (rather than passing through) when no tenant is supplied. Without\n // this, a tenant set via SMRT_MCP_TENANT_ID would only set async context\n // with no interceptor to enforce it.\n enableTenancy();\n`\n : ''\n}\n // Load configuration from environment and .smrt.config files\n const appConfig = await config.load();\n const aiConfig = appConfig?.ai || {};\n\n if (DEBUG) {\n console.error(\\`[MCP] Loaded \\${TOOLS.length} static tools\\`);\n console.error(\\`[MCP] Available tools:\\`, TOOLS.map(t => t.name).join(', '));\n }\n\n // Create MCP server\n const server = new Server(\n {\n name: SERVER_NAME,\n version: SERVER_VERSION,\n },\n {\n capabilities: {\n tools: {},\n },\n }\n );\n\n // Register ListTools handler\n server.setRequestHandler(ListToolsRequestSchema, async (_request: ListToolsRequest) => {\n if (DEBUG) {\n console.error(\\`[MCP] ListTools request received\\`);\n }\n\n return {\n tools: TOOLS,\n };\n });\n\n // Register CallTool handler\n server.setRequestHandler(CallToolRequestSchema, async (request: CallToolRequest) => {\n const { name: toolName, arguments: args = {} } = request.params;\n\n if (DEBUG) {\n console.error(\\`[MCP] CallTool request: \\${toolName}\\`);\n console.error(\\`[MCP] Arguments:\\`, JSON.stringify(args, null, 2));\n }\n\n try {\n // Static switch statement for tool execution\n const runToolBody = async () => {\n switch (toolName) {\n${switchCases}\n\n default:\n throw new Error(\\`Unknown tool: \\${toolName}\\`);\n }\n };\n${\n hasTenantScoped\n ? `\n // Fail-closed tenant context for tenant-scoped tools (#1554).\n const [toolObject] = toolName.split('_');\n const result =\n toolObject && TENANT_SCOPED.has(toolObject.toLowerCase())\n ? await runTenantScopedEntryPoint(\n { tenantScoped: true, tenantId: MCP_TENANT_ID, allowCrossTenant: MCP_ALLOW_CROSS_TENANT, surface: 'MCP' },\n runToolBody,\n )\n : await runToolBody();`\n : `\n const result = await runToolBody();`\n}\n\n if (DEBUG) {\n console.error(\\`[MCP] Tool executed successfully: \\${toolName}\\`);\n }\n\n return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n console.error(\\`[MCP] Tool execution failed: \\${toolName}\\`, error);\n\n return {\n content: [\n {\n type: 'text',\n text: \\`Error executing tool \\${toolName}: \\${errorMessage}\\`,\n },\n ],\n isError: true,\n };\n }\n });\n\n // Setup stdio transport\n const transport = new StdioServerTransport();\n\n // Connect server to transport\n await server.connect(transport);\n\n if (DEBUG) {\n console.error(\\`[MCP] Server connected via stdio transport\\`);\n console.error(\\`[MCP] Ready to receive requests\\`);\n }\n\n // Handle graceful shutdown\n process.on('SIGINT', async () => {\n if (DEBUG) {\n console.error(\\`[MCP] Received SIGINT, shutting down gracefully\\`);\n }\n await server.close();\n process.exit(0);\n });\n\n process.on('SIGTERM', async () => {\n if (DEBUG) {\n console.error(\\`[MCP] Received SIGTERM, shutting down gracefully\\`);\n }\n await server.close();\n process.exit(0);\n });\n } catch (error) {\n console.error('[MCP] Fatal error during server startup:', error);\n process.exit(1);\n }\n}\n\n// Start the server\nmain().catch((error) => {\n console.error('[MCP] Unhandled error:', error);\n process.exit(1);\n});\n`;\n}\n\n/**\n * Generate package.json script for running MCP server\n *\n * @param serverPath - Path to generated server file (relative to package root)\n * @returns Script command for package.json\n */\nexport function generateMCPScript(\n serverPath: string = 'dist/mcp-server.js',\n): string {\n return `node ${serverPath}`;\n}\n\n/**\n * Generate Claude Desktop configuration example\n *\n * @param serverName - Name for the MCP server\n * @param serverPath - Absolute path to server file\n * @returns Configuration object for claude_desktop_config.json\n */\nexport function generateClaudeConfig(\n serverName: string,\n serverPath: string,\n): object {\n return {\n mcpServers: {\n [serverName]: {\n command: 'node',\n args: [serverPath],\n },\n },\n };\n}\n\n/**\n * Generate README documentation for MCP server setup\n *\n * @param serverName - Name of the MCP server\n * @param serverPath - Path to the server file\n * @returns Markdown documentation\n */\nexport function generateMCPDocumentation(\n serverName: string,\n serverPath: string,\n): string {\n return `# MCP Server Setup\n\nThis project includes an auto-generated MCP (Model Context Protocol) server that exposes SMRT objects as tools for AI integration.\n\n## Quick Start\n\n### 1. Build the MCP Server\n\n\\`\\`\\`bash\nnpm run build\n\\`\\`\\`\n\nThis generates the MCP server at: \\`${serverPath}\\`\n\n### 2. Configure Claude Desktop\n\nAdd the following to your Claude Desktop configuration file:\n\n**macOS**: \\`~/.config/Claude/claude_desktop_config.json\\`\n**Windows**: \\`%APPDATA%\\\\Claude\\\\claude_desktop_config.json\\`\n\n\\`\\`\\`json\n{\n \"mcpServers\": {\n \"${serverName}\": {\n \"command\": \"node\",\n \"args\": [\"/absolute/path/to/${serverPath}\"]\n }\n }\n}\n\\`\\`\\`\n\nReplace \\`/absolute/path/to/\\` with the actual absolute path to your project directory.\n\n### 3. Restart Claude Desktop\n\nClose and reopen Claude Desktop to load the new MCP server.\n\n### 4. Test the Integration\n\nIn Claude Code, you can now use the auto-generated tools. For example:\n\n- \\`list_products\\` - List all products\n- \\`get_product\\` - Get a specific product by ID\n- \\`create_product\\` - Create a new product\n- And more...\n\n## Environment Variables\n\nThe MCP server supports optional environment variables:\n\n- \\`DATABASE_URL\\` - Database connection string\n\n**AI Provider Configuration (in priority order):**\n1. **Generic configuration** (supports any provider):\n - \\`SMRT_AI_PROVIDER\\` - Provider name (e.g., 'openai', 'anthropic', 'claude-cli', 'gemini')\n - \\`SMRT_AI_API_KEY\\` - API key for the provider\n - \\`SMRT_AI_MODEL\\` - Model to use (optional)\n\n2. **Provider-specific fallbacks**:\n - \\`OPENAI_API_KEY\\` - OpenAI API key (auto-detects provider as 'openai')\n - \\`ANTHROPIC_API_KEY\\` - Anthropic API key (auto-detects provider as 'anthropic')\n - \\`CLAUDE_API_KEY\\` + \\`CLAUDE_MODEL\\` - Claude CLI provider (defaults to 'sonnet')\n\n**Examples:**\n\\`\\`\\`bash\n# Using generic configuration (recommended)\nexport SMRT_AI_PROVIDER=claude-cli\nexport SMRT_AI_MODEL=sonnet\n\n# Using provider-specific configuration\nexport CLAUDE_API_KEY=your-key\nexport CLAUDE_MODEL=sonnet\n\n# Using OpenAI\nexport OPENAI_API_KEY=your-openai-key\n\\`\\`\\`\n\n## Troubleshooting\n\n### Server Not Appearing in Claude\n\n1. Check that the path in \\`claude_desktop_config.json\\` is absolute\n2. Verify the server file exists at the specified path\n3. Check Claude Desktop logs for errors\n\n### Tools Not Working\n\n1. Ensure your database is accessible (if using one)\n2. Check that SMRT objects are properly decorated with \\`@smrt()\\`\n3. Look for errors in the MCP server output\n\n### Debug Mode\n\nTo enable debug logging, set the \\`DEBUG\\` constant to \\`true\\` in the generated server file.\n\n## Generated Tools\n\nThe following tools are automatically generated from your SMRT objects:\n\n- **CRUD Operations**: \\`list_\\`, \\`get_\\`, \\`create_\\`, \\`update_\\`, \\`delete_\\` for each object type\n- **Custom Actions**: Any custom methods included in the \\`@smrt()\\` decorator configuration\n\nSee the SMRT object definitions for the complete list of available tools and their parameters.\n`;\n}\n"],"names":[],"mappings":"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"}
|
package/dist/generators/mcp.d.ts
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Exposes smrt objects as AI tools for Claude, GPT, and other AI models
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* A JSON Schema fragment for an MCP tool input property. The shape varies by
|
|
8
|
+
* field kind (string/number/object/…), so values are `unknown`; this is only
|
|
9
|
+
* ever serialized into the generated tool definitions, never read back.
|
|
10
|
+
*/
|
|
11
|
+
type McpJsonSchema = Record<string, unknown>;
|
|
12
|
+
/**
|
|
13
|
+
* Runtime tool-call arguments. They arrive as untyped JSON from the MCP client,
|
|
14
|
+
* so individual keys are narrowed (`as`) at each action's boundary.
|
|
15
|
+
*/
|
|
16
|
+
type ToolArgs = Record<string, unknown>;
|
|
6
17
|
export interface MCPConfig {
|
|
7
18
|
name?: string;
|
|
8
19
|
version?: string;
|
|
@@ -13,8 +24,8 @@ export interface MCPConfig {
|
|
|
13
24
|
};
|
|
14
25
|
}
|
|
15
26
|
export interface MCPContext {
|
|
16
|
-
db?:
|
|
17
|
-
ai?:
|
|
27
|
+
db?: unknown;
|
|
28
|
+
ai?: unknown;
|
|
18
29
|
user?: {
|
|
19
30
|
id: string;
|
|
20
31
|
roles?: string[];
|
|
@@ -38,7 +49,7 @@ export interface MCPTool {
|
|
|
38
49
|
description: string;
|
|
39
50
|
inputSchema: {
|
|
40
51
|
type: string;
|
|
41
|
-
properties: Record<string,
|
|
52
|
+
properties: Record<string, McpJsonSchema>;
|
|
42
53
|
required?: string[];
|
|
43
54
|
};
|
|
44
55
|
}
|
|
@@ -46,7 +57,7 @@ export interface MCPRequest {
|
|
|
46
57
|
method: string;
|
|
47
58
|
params: {
|
|
48
59
|
name: string;
|
|
49
|
-
arguments:
|
|
60
|
+
arguments: ToolArgs;
|
|
50
61
|
};
|
|
51
62
|
}
|
|
52
63
|
export interface MCPResponse {
|
|
@@ -228,4 +239,5 @@ export declare class MCPGenerator {
|
|
|
228
239
|
*/
|
|
229
240
|
private generateModularIndex;
|
|
230
241
|
}
|
|
242
|
+
export {};
|
|
231
243
|
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/generators/mcp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../../src/generators/mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/generators/mcp.js
CHANGED
|
@@ -413,7 +413,10 @@ class MCPGenerator {
|
|
|
413
413
|
*/
|
|
414
414
|
toPublicData(value, seen = /* @__PURE__ */ new WeakSet()) {
|
|
415
415
|
if (value === null || typeof value !== "object") return value;
|
|
416
|
-
|
|
416
|
+
const publicSource = value;
|
|
417
|
+
if (typeof publicSource.toPublicJSON === "function") {
|
|
418
|
+
return publicSource.toPublicJSON();
|
|
419
|
+
}
|
|
417
420
|
if (Array.isArray(value)) {
|
|
418
421
|
if (seen.has(value)) return value;
|
|
419
422
|
seen.add(value);
|
|
@@ -562,7 +565,9 @@ class MCPGenerator {
|
|
|
562
565
|
listOptions.orderBy = args.orderBy;
|
|
563
566
|
}
|
|
564
567
|
const results = await collection.list(listOptions);
|
|
565
|
-
const total = await collection.count({
|
|
568
|
+
const total = await collection.count({
|
|
569
|
+
where: args.where || {}
|
|
570
|
+
});
|
|
566
571
|
return {
|
|
567
572
|
data: results.map((result) => this.toPublicData(result)),
|
|
568
573
|
meta: {
|
|
@@ -593,12 +598,14 @@ class MCPGenerator {
|
|
|
593
598
|
createData.created_by = this.context.user.id;
|
|
594
599
|
createData.owner_id = this.context.user.id;
|
|
595
600
|
}
|
|
596
|
-
const newItem = await collection.create(
|
|
601
|
+
const newItem = await collection.create(
|
|
602
|
+
createData
|
|
603
|
+
);
|
|
597
604
|
await newItem.save();
|
|
598
605
|
return this.toPublicData(newItem);
|
|
599
606
|
}
|
|
600
607
|
case "update": {
|
|
601
|
-
const
|
|
608
|
+
const id = args.id;
|
|
602
609
|
if (!id) {
|
|
603
610
|
throw new Error("ID is required for update");
|
|
604
611
|
}
|
|
@@ -635,7 +642,8 @@ class MCPGenerator {
|
|
|
635
642
|
* Execute a custom action on a collection/object
|
|
636
643
|
*/
|
|
637
644
|
async executeCustomAction(collection, action, args) {
|
|
638
|
-
const { id, options = {}, ...directArgs } = args;
|
|
645
|
+
const { id, options: rawOptions = {}, ...directArgs } = args;
|
|
646
|
+
const options = rawOptions;
|
|
639
647
|
try {
|
|
640
648
|
if (id) {
|
|
641
649
|
const object = await collection.get(id);
|
|
@@ -643,17 +651,25 @@ class MCPGenerator {
|
|
|
643
651
|
throw new Error("Object not found");
|
|
644
652
|
}
|
|
645
653
|
const objectWithMethods = object;
|
|
646
|
-
|
|
654
|
+
const objectMethod = objectWithMethods[action];
|
|
655
|
+
if (typeof objectMethod === "function") {
|
|
647
656
|
const methodArgs = Object.keys(options).length > 0 ? options : directArgs;
|
|
648
|
-
const result = await
|
|
657
|
+
const result = await objectMethod.call(
|
|
658
|
+
object,
|
|
659
|
+
methodArgs
|
|
660
|
+
);
|
|
649
661
|
return result;
|
|
650
662
|
} else {
|
|
651
663
|
throw new Error(`Method '${action}' not found on object instance`);
|
|
652
664
|
}
|
|
653
665
|
} else {
|
|
654
|
-
|
|
666
|
+
const collectionMethod = collection[action];
|
|
667
|
+
if (typeof collectionMethod === "function") {
|
|
655
668
|
const methodArgs = Object.keys(options).length > 0 ? options : directArgs;
|
|
656
|
-
const result = await
|
|
669
|
+
const result = await collectionMethod.call(
|
|
670
|
+
collection,
|
|
671
|
+
methodArgs
|
|
672
|
+
);
|
|
657
673
|
return result;
|
|
658
674
|
} else {
|
|
659
675
|
throw new Error(
|