@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":"mcp.js","sources":["../../src/generators/mcp.ts"],"sourcesContent":["/**\n * MCP (Model Context Protocol) server generator for smrt objects\n *\n * Exposes smrt objects as AI tools for Claude, GPT, and other AI models\n */\n\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { SmrtCollection } from '../collection';\nimport type { SmrtObject } from '../object';\nimport { ObjectRegistry } from '../registry';\nimport {\n generateClaudeConfig,\n generateMCPDocumentation,\n generateMCPScript,\n generateRuntimeBootstrap,\n type RuntimeOptions,\n} from './mcp-runtime-template.js';\nimport { runWithTenantGate } from './tenant-gate.js';\n\nexport interface MCPConfig {\n name?: string;\n version?: string;\n description?: string;\n server?: {\n name: string;\n version: string;\n };\n}\n\nexport interface MCPContext {\n db?: any;\n ai?: any;\n user?: {\n id: string;\n roles?: string[];\n };\n /**\n * Tenant the calling principal is scoped to (#1554). When set, tenant-scoped\n * tools run inside this tenant's context. Hosts that authenticate a principal\n * (e.g. `@happyvertical/smrt-app-mcp`) should derive it from the principal —\n * the MCP analogue of the SvelteKit auth hook setting `locals.tenantId`.\n */\n tenantId?: string;\n /**\n * Explicit operator opt-in to cross-tenant access for tenant-scoped tools\n * (#1554). Only set for trusted operator/admin callers. Without a `tenantId`\n * or this flag, tenant-scoped tool calls fail closed when tenancy is enabled.\n */\n allowCrossTenant?: boolean;\n}\n\nexport interface MCPTool {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, any>;\n required?: string[];\n };\n}\n\nexport interface MCPRequest {\n method: string;\n params: {\n name: string;\n arguments: Record<string, any>;\n };\n}\n\nexport interface MCPResponse {\n content: Array<{\n type: 'text';\n text: string;\n }>;\n}\n\n/**\n * Generate MCP server from smrt objects\n */\nexport class MCPGenerator {\n private config: MCPConfig;\n private context: MCPContext;\n private collections = new Map<string, SmrtCollection<any>>();\n\n constructor(config: MCPConfig = {}, context: MCPContext = {}) {\n this.config = {\n name: 'smrt-mcp-server',\n version: '1.0.0',\n description: 'Auto-generated MCP server from smrt objects',\n server: {\n name: 'smrt-mcp',\n version: '1.0.0',\n },\n ...config,\n };\n this.context = context;\n }\n\n /**\n * Get server name\n */\n get name(): string | undefined {\n return this.config.name;\n }\n\n /**\n * Get server version\n */\n get version(): string | undefined {\n return this.config.version;\n }\n\n /**\n * Generate all available tools from registered objects\n */\n async generateTools(): Promise<MCPTool[]> {\n const tools: MCPTool[] = [];\n const registeredClasses = ObjectRegistry.getAllClasses();\n\n for (const [key, classInfo] of registeredClasses) {\n // Issue #951: Use simple name for tool naming, map key for registry lookups\n const simpleName = classInfo.name || key;\n const config = ObjectRegistry.getConfig(simpleName);\n const mcpConfig = config.mcp;\n\n // `mcp: false` disables MCP generation entirely for the class. Without\n // this gate an `include` list still leaked custom-method tools (the\n // custom-method branch historically only honored `include` when it\n // listed custom methods), so `false` is the only fully fail-closed\n // switch. Mirrors rest.ts's `apiConfig === false` short-circuit\n // (#1540 / #1546).\n if (mcpConfig === false) {\n continue;\n }\n\n // Handle boolean vs object config\n const excluded: string[] =\n typeof mcpConfig === 'object' && mcpConfig?.exclude\n ? mcpConfig.exclude\n : [];\n const included: string[] | undefined =\n typeof mcpConfig === 'object' ? mcpConfig?.include : undefined;\n\n const shouldInclude = (endpoint: string) => {\n if (included && !included.includes(endpoint)) return false;\n if (excluded.includes(endpoint)) return false;\n return true;\n };\n\n const objectTools = await this.generateObjectTools(\n simpleName,\n shouldInclude,\n );\n tools.push(...objectTools);\n }\n\n return tools;\n }\n\n /**\n * Generate tools for a specific object\n */\n private async generateObjectTools(\n objectName: string,\n shouldInclude: (endpoint: string) => boolean,\n ): Promise<MCPTool[]> {\n const tools: MCPTool[] = [];\n const fields = ObjectRegistry.getFields(objectName);\n const lowerName = objectName.toLowerCase();\n const classInfo = ObjectRegistry.getClass(objectName);\n\n // LIST tool\n if (shouldInclude('list')) {\n tools.push({\n name: `${lowerName}_list`,\n description: `List ${objectName} objects with optional filtering`,\n inputSchema: {\n type: 'object',\n properties: {\n limit: {\n type: 'integer',\n description: 'Maximum number of items to return',\n default: 50,\n minimum: 1,\n maximum: 1000,\n },\n offset: {\n type: 'integer',\n description: 'Number of items to skip',\n default: 0,\n minimum: 0,\n },\n orderBy: {\n type: 'string',\n description: 'Field to order by (e.g., \"created_at DESC\")',\n },\n where: {\n type: 'object',\n description: 'Filter conditions as key-value pairs',\n additionalProperties: true,\n },\n },\n },\n });\n }\n\n // GET tool\n if (shouldInclude('get')) {\n tools.push({\n name: `${lowerName}_get`,\n description: `Get a specific ${objectName} by ID or slug`,\n inputSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier of the object',\n },\n slug: {\n type: 'string',\n description: 'URL-friendly identifier of the object',\n },\n },\n required: ['id'],\n },\n });\n }\n\n // CREATE tool\n if (shouldInclude('create')) {\n const properties: Record<string, any> = {};\n const required: string[] = [];\n\n for (const [fieldName, field] of fields) {\n properties[fieldName] = this.fieldToMCPSchema(field);\n if (field._meta?.required) {\n required.push(fieldName);\n }\n }\n\n tools.push({\n name: `${lowerName}_create`,\n description: `Create a new ${objectName}`,\n inputSchema: {\n type: 'object',\n properties,\n required,\n },\n });\n }\n\n // UPDATE tool\n if (shouldInclude('update')) {\n const properties: Record<string, any> = {\n id: {\n type: 'string',\n description: 'ID of the object to update',\n },\n };\n\n for (const [fieldName, field] of fields) {\n properties[fieldName] = this.fieldToMCPSchema(field);\n }\n\n tools.push({\n name: `${lowerName}_update`,\n description: `Update an existing ${objectName}`,\n inputSchema: {\n type: 'object',\n properties,\n required: ['id'],\n },\n });\n }\n\n // DELETE tool\n if (shouldInclude('delete')) {\n tools.push({\n name: `${lowerName}_delete`,\n description: `Delete a ${objectName} by ID`,\n inputSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'ID of the object to delete',\n },\n },\n required: ['id'],\n },\n });\n }\n\n // CUSTOM METHODS - discover from manifest and show by default\n if (classInfo) {\n const config = ObjectRegistry.getConfig(objectName);\n const mcpConfig = config.mcp;\n const included: string[] | undefined =\n typeof mcpConfig === 'object' ? mcpConfig?.include : undefined;\n const excluded: string[] =\n typeof mcpConfig === 'object' && mcpConfig?.exclude\n ? mcpConfig.exclude\n : [];\n\n // When an `include` list is present it is the COMPLETE allowlist for\n // this surface: a custom (non-CRUD) method is exposed ONLY if its name\n // appears in `include`. Without an include list we keep the historical\n // default of auto-exposing every public method. This closes the leak\n // where `mcp: { include: ['list', 'get'] }` still emitted custom-method\n // tools like `payment_recordpayment` because `include` only gated CRUD\n // verbs (#1540 / #1390).\n const crudOperations = ['list', 'get', 'create', 'update', 'delete'];\n const customMethodsInInclude =\n included?.filter((item) => !crudOperations.includes(item)) || [];\n // An include list (even one naming only CRUD verbs) switches custom\n // methods into strict allowlist mode.\n const hasIncludeList = included !== undefined;\n\n // Try to discover methods from manifest (including inherited methods)\n const methods = await ObjectRegistry.getAllMethods(objectName);\n const methodNames = new Set(Array.from(methods.keys()));\n\n // Strict mode: an include list is present, so only the custom methods it\n // names are generated (may be none, e.g. include: ['list', 'get']).\n if (hasIncludeList) {\n for (const methodName of customMethodsInInclude) {\n // Skip if explicitly excluded\n if (excluded.includes(methodName)) continue;\n\n // Check if method exists (in manifest or on class prototype)\n const existsInManifest = methodNames.has(methodName);\n const existsOnClass = this.validateCustomMethod(\n classInfo.constructor,\n methodName,\n );\n\n if (!existsInManifest && !existsOnClass) {\n // Warn about missing methods\n console.warn(\n `Warning: Custom action '${methodName}' specified in MCP config for ${objectName}, but method ${methodName}() not found on class`,\n );\n continue;\n }\n\n // A non-public method must never be exposed as a tool, even when it\n // is explicitly named in `include`. This keeps strict-include mode\n // consistent with the non-strict path below, which gates on\n // `methodDef.isPublic`. Listing a private method in `include` is a\n // config mistake, not an override of method visibility (#1540).\n //\n // The scanner strips private/protected methods from the manifest, so\n // when a non-public method is named in `include` it is absent from\n // `methods` and is only resolvable via validateCustomMethod() on the\n // runtime prototype (TS access modifiers are erased at runtime). Such\n // a method must NOT be emitted. We still allow methods that are\n // present on the class but legitimately absent from the manifest\n // (e.g. inline/dynamically registered classes), so the guard fires\n // only when there is a public manifest entry to anchor on OR the\n // method exists solely as a stripped (non-public) manifest method.\n const methodDef = methods.get(methodName);\n if (methodDef && !methodDef.isPublic) continue;\n\n // Generate tool for this method\n const toolName = `${lowerName}_${methodName}`.toLowerCase();\n tools.push({\n name: toolName,\n description: `Execute ${methodName} action on ${objectName}`,\n inputSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'ID of the object to execute action on',\n },\n options: {\n type: 'object',\n description: 'Additional options for the custom action',\n additionalProperties: true,\n },\n },\n required: ['id'],\n },\n });\n }\n } else {\n // No custom methods in include = show all discovered methods by default\n for (const [methodName, methodDef] of methods) {\n // Skip if not public (private/protected methods shouldn't be in MCP)\n if (!methodDef.isPublic) continue;\n\n // Always respect exclude list\n if (excluded.includes(methodName)) continue;\n\n // Generate MCP tool for this custom method\n const toolName = `${lowerName}_${methodName}`.toLowerCase();\n tools.push({\n name: toolName,\n description: `Execute ${methodName} action on ${objectName}`,\n inputSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'ID of the object to execute action on',\n },\n options: {\n type: 'object',\n description: 'Additional options for the custom action',\n additionalProperties: true,\n },\n },\n required: ['id'],\n },\n });\n }\n }\n }\n\n return tools;\n }\n\n /**\n * Validate that a custom method exists on a class\n */\n private validateCustomMethod(\n classConstructor: typeof SmrtObject,\n methodName: string,\n ): boolean {\n try {\n // Check if method exists on the prototype\n const prototype = classConstructor.prototype;\n\n // Check if the method exists and is a function\n if (typeof (prototype as any)[methodName] === 'function') {\n return true;\n }\n\n // Also check static methods\n if (typeof (classConstructor as any)[methodName] === 'function') {\n return true;\n }\n\n return false;\n } catch (error) {\n console.warn(\n `Error validating method ${methodName} on class ${classConstructor.name}:`,\n error,\n );\n return false;\n }\n }\n\n /**\n * Convert field definition to MCP schema\n */\n private fieldToMCPSchema(field: any): any {\n const schema: any = {\n description: field._meta?.description || `${field.type} field`,\n };\n\n switch (field.type) {\n case 'text':\n schema.type = 'string';\n if (field._meta?.maxLength) schema.maxLength = field._meta.maxLength;\n if (field._meta?.minLength) schema.minLength = field._meta.minLength;\n break;\n case 'integer':\n schema.type = 'integer';\n if (field._meta?.min !== undefined) schema.minimum = field._meta.min;\n if (field._meta?.max !== undefined) schema.maximum = field._meta.max;\n break;\n case 'decimal':\n schema.type = 'number';\n if (field._meta?.min !== undefined) schema.minimum = field._meta.min;\n if (field._meta?.max !== undefined) schema.maximum = field._meta.max;\n break;\n case 'boolean':\n schema.type = 'boolean';\n break;\n case 'datetime':\n schema.type = 'string';\n schema.format = 'date-time';\n break;\n case 'json':\n schema.type = 'object';\n break;\n case 'foreignKey':\n schema.type = 'string';\n schema.description = `ID of related ${field.related || 'object'}`;\n break;\n default:\n schema.type = 'string';\n }\n\n if (field._meta?.default !== undefined) {\n schema.default = field._meta.default;\n }\n\n return schema;\n }\n\n /**\n * Handle MCP tool calls\n */\n async handleToolCall(request: MCPRequest): Promise<MCPResponse> {\n const { name, arguments: args } = request.params;\n\n try {\n // Check if tool exists\n const availableTools = await this.generateTools();\n const toolExists = availableTools.some((t) => t.name === name);\n\n if (!toolExists) {\n throw new Error(`Unknown tool: ${name}`);\n }\n\n // Parse tool name: `objectname_action`. Split on the FIRST underscore\n // only — a custom method name can itself contain underscores (e.g.\n // `record_payment` → tool `invoice_record_payment`). A naive\n // `name.split('_')` would take `action` as just `record` and mis-route\n // the call. The emitted stdio servers switch on the full tool name, so\n // splitting greedily here also kept the in-process path divergent (#1378).\n const firstUnderscore = name.indexOf('_');\n const objectName =\n firstUnderscore === -1 ? '' : name.slice(0, firstUnderscore);\n const action =\n firstUnderscore === -1 ? '' : name.slice(firstUnderscore + 1);\n\n if (!objectName || !action) {\n throw new Error(`Invalid tool name format: ${name}`);\n }\n\n // Find the registered class (case-insensitive)\n const registeredClasses = ObjectRegistry.getAllClasses();\n let classInfo = null;\n let actualObjectName = '';\n\n for (const [_key, info] of registeredClasses) {\n // Issue #951: Match by simple name, not the qualified map key\n const simpleName = info.name || _key;\n if (simpleName.toLowerCase() === objectName.toLowerCase()) {\n classInfo = info;\n actualObjectName = simpleName;\n break;\n }\n }\n\n if (!classInfo) {\n throw new Error(`Object type '${objectName}' not found`);\n }\n\n // Get or create collection\n const collection = await this.getCollection(actualObjectName, classInfo);\n\n // Execute the action\n const result = await this.executeAction(\n collection,\n action,\n args,\n actualObjectName,\n );\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n };\n }\n }\n\n /**\n * Get or create collection for an object\n */\n private async getCollection(\n objectName: string,\n classInfo: any,\n ): Promise<SmrtCollection<any>> {\n if (!this.collections.has(objectName)) {\n // Ensure we have a valid collection constructor\n if (\n !classInfo.collectionConstructor ||\n typeof classInfo.collectionConstructor !== 'function'\n ) {\n throw new Error(\n `No valid collection constructor found for ${objectName}`,\n );\n }\n\n const collection = new classInfo.collectionConstructor({\n ai: this.context.ai,\n db: this.context.db,\n });\n\n // Verify the collection is actually a SmrtCollection instance\n if (!(collection instanceof SmrtCollection)) {\n throw new Error(\n `Collection for ${objectName} must extend SmrtCollection`,\n );\n }\n\n // Initialize the collection (database setup, etc.)\n await collection.initialize();\n\n this.collections.set(objectName, collection);\n }\n const collection = this.collections.get(objectName);\n if (!collection) {\n throw new Error(`Collection for ${objectName} not found`);\n }\n return collection;\n }\n\n /**\n * Serialize a tool-response payload, excluding sensitive fields (#1540).\n * Recurses through arrays and plain objects so a SmrtObject nested inside a\n * custom-action result (e.g. `{ item }`) is also stripped — `JSON.stringify`\n * would otherwise call its `toJSON()`. Non-plain instances (Date, etc.) and\n * primitives pass through unchanged; 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 /**\n * Mass-assignment guard (#1540): strip framework/server-managed and\n * `@field({ readonly: true })` fields from a create/update body, and — when an\n * `@smrt({ api: { writable: [...] } })` allowlist is set — intersect with it.\n */\n private applyWritablePolicy(\n objectName: string | undefined,\n data: any,\n ): Record<string, any> {\n if (!data || typeof data !== 'object') {\n return {};\n }\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 * Execute action on collection\n */\n /**\n * Fail-closed authorization for tool calls (#1540). Mutating tools\n * (create/update/delete + custom actions) require an authenticated principal\n * (`context.user`) unless the object opts out via `@smrt({ api: { public } })`.\n * Reads are allowed when `public` is `true` or `'read'`.\n */\n private requireToolAuth(\n objectName: string | undefined,\n mutating: boolean,\n ): void {\n const apiConfig = objectName\n ? ObjectRegistry.getConfig(objectName)?.api\n : undefined;\n const publicAccess =\n apiConfig && typeof apiConfig === 'object'\n ? (apiConfig as { public?: boolean | 'read' }).public\n : undefined;\n\n if (publicAccess === true) return;\n if (publicAccess === 'read' && !mutating) return;\n if (!this.context.user) {\n throw new Error('Authentication required');\n }\n }\n\n private async executeAction(\n collection: SmrtCollection<any>,\n action: string,\n args: any,\n objectName?: string,\n ): Promise<any> {\n const mutating = action !== 'list' && action !== 'get';\n this.requireToolAuth(objectName, mutating);\n\n // Fail-closed tenant context (#1554). For tenant-scoped objects, establish\n // the context from the principal's tenant (or an explicit cross-tenant\n // opt-in); without either, this throws when tenancy is enabled rather than\n // letting an optional-scoped read range across all tenants. Tenant-scoping\n // is resolved inside tenancy by class name so it matches the interceptor.\n return runWithTenantGate(\n {\n className: objectName,\n tenantId: this.context.tenantId,\n allowCrossTenant: this.context.allowCrossTenant,\n surface: 'MCP',\n },\n () => this.runAction(collection, action, args, objectName),\n );\n }\n\n /**\n * Derive the set of tenant-scoped object names (lowercased simple names) from\n * a generated tool list, for the emitted runtime template's tenant gate\n * (#1554). Tool names are `objectname_action`.\n *\n * Detection uses ONLY tenancy's `isTenantScopedClass` (the authoritative\n * source the interceptor uses; covers `@TenantScoped`), consulted via an\n * optional dynamic import. We deliberately do NOT fall back to core's\n * `ObjectRegistry.isTenantScoped`: a `@smrt({ tenantScoped })` model can exist\n * in an app that has NOT installed `@happyvertical/smrt-tenancy`, and emitting\n * the static `import { runTenantScopedEntryPoint } from '@happyvertical/smrt-tenancy'`\n * gate for it would crash the generated server at import time. When tenancy is\n * absent there is also nothing to enforce, so emitting no gate is correct.\n */\n private async tenantScopedObjectNames(tools: MCPTool[]): Promise<string[]> {\n let isTenantScopedClass: ((name: string) => boolean) | undefined;\n try {\n // Held in a variable so neither TypeScript's declaration emit (TS2307 —\n // core deliberately does not depend on tenancy) nor the bundler tries to\n // statically resolve this optional sibling package. Same pattern as\n // `tenant-gate.ts` / `embeddings/provider.ts`.\n const tenancySpecifier = '@happyvertical/smrt-tenancy';\n const tenancy = (await import(/* @vite-ignore */ tenancySpecifier)) as {\n isTenantScopedClass?: (name: string) => boolean;\n };\n isTenantScopedClass = tenancy.isTenantScopedClass;\n } catch {\n isTenantScopedClass = undefined;\n }\n\n // No tenancy package installed → no gate can run, emit none.\n if (typeof isTenantScopedClass !== 'function') return [];\n\n const scoped = new Set<string>();\n for (const tool of tools) {\n const [objectName] = tool.name.split('_');\n if (!objectName) continue;\n // Resolve the registered simple name (case-insensitive) and test scoping.\n for (const [key, info] of ObjectRegistry.getAllClasses()) {\n const simpleName = info.name || key;\n if (simpleName.toLowerCase() === objectName.toLowerCase()) {\n if (isTenantScopedClass(simpleName)) {\n scoped.add(simpleName.toLowerCase());\n }\n break;\n }\n }\n }\n return Array.from(scoped);\n }\n\n /**\n * Execute a resolved MCP action (CRUD or custom) against a collection. Always\n * invoked inside the tenant gate established by {@link executeAction}.\n */\n private async runAction(\n collection: SmrtCollection<any>,\n action: string,\n args: any,\n objectName?: string,\n ): Promise<any> {\n switch (action) {\n case 'list': {\n const listOptions: any = {\n limit: Math.min(args.limit || 50, 1000),\n offset: args.offset || 0,\n };\n\n if (args.where) {\n listOptions.where = args.where;\n }\n\n if (args.orderBy) {\n listOptions.orderBy = args.orderBy;\n }\n\n const results = await collection.list(listOptions);\n const total = await collection.count({ where: args.where || {} });\n\n return {\n data: results.map((result: any) => this.toPublicData(result)),\n meta: {\n total,\n limit: listOptions.limit,\n offset: listOptions.offset,\n count: results.length,\n },\n };\n }\n\n case 'get': {\n if (!args.id && !args.slug) {\n throw new Error('Either id or slug is required');\n }\n\n const filter = args.id ? args.id : args.slug;\n const item = await collection.get(filter);\n\n if (!item) {\n throw new Error('Object not found');\n }\n\n return this.toPublicData(item);\n }\n\n case 'create': {\n // Mass-assignment guard (#1540): only writable fields from the caller.\n const createData: Record<string, any> = this.applyWritablePolicy(\n objectName,\n args,\n );\n // Server-set ownership context (not caller-controlled).\n if (this.context.user) {\n createData.created_by = this.context.user.id;\n createData.owner_id = this.context.user.id;\n }\n\n const newItem = await collection.create(createData);\n await newItem.save();\n\n return this.toPublicData(newItem);\n }\n\n case 'update': {\n const { id } = args;\n if (!id) {\n throw new Error('ID is required for update');\n }\n\n const existing = await collection.get(id);\n if (!existing) {\n throw new Error('Object not found');\n }\n\n // Mass-assignment guard (#1540): strip server-managed/read-only keys\n // (incl. `id`) before applying caller-supplied updates.\n const updateData = this.applyWritablePolicy(objectName, args);\n Object.assign(existing, updateData);\n\n // Add user context\n if (this.context.user) {\n (existing as any).updated_by = this.context.user.id;\n }\n\n await existing.save();\n\n return this.toPublicData(existing);\n }\n\n case 'delete': {\n if (!args.id) {\n throw new Error('ID is required for delete');\n }\n\n const toDelete = await collection.get(args.id);\n if (!toDelete) {\n throw new Error('Object not found');\n }\n\n await toDelete.delete();\n\n return { success: true, message: 'Object deleted successfully' };\n }\n\n default: {\n // Handle custom actions. The method may return a SmrtObject (or array),\n // so serialize through toPublicData to strip sensitive fields (#1540).\n const result = await this.executeCustomAction(collection, action, args);\n return this.toPublicData(result);\n }\n }\n }\n\n /**\n * Execute a custom action on a collection/object\n */\n private async executeCustomAction(\n collection: SmrtCollection<any>,\n action: string,\n args: any,\n ): Promise<any> {\n const { id, options = {}, ...directArgs } = args;\n\n try {\n // If an ID is provided, get the specific object and call the method on it\n if (id) {\n const object = await collection.get(id);\n if (!object) {\n throw new Error('Object not found');\n }\n\n // Check if the method exists on the object instance\n // Cast to any to access dynamic method names\n const objectWithMethods = object as any;\n if (typeof objectWithMethods[action] === 'function') {\n // Call the method with the provided options\n // If options is provided, use it; otherwise use directArgs\n const methodArgs =\n Object.keys(options).length > 0 ? options : directArgs;\n const result = await objectWithMethods[action](methodArgs);\n return result;\n } else {\n throw new Error(`Method '${action}' not found on object instance`);\n }\n } else {\n // No ID provided, try to call the method on the collection\n if (typeof (collection as any)[action] === 'function') {\n const methodArgs =\n Object.keys(options).length > 0 ? options : directArgs;\n const result = await (collection as any)[action](methodArgs);\n return result;\n } else {\n throw new Error(\n `Method '${action}' not found on collection. For object-specific actions, provide an 'id' parameter.`,\n );\n }\n }\n } catch (error) {\n throw new Error(\n `Failed to execute custom action '${action}': ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Generate MCP server info\n */\n getServerInfo() {\n return {\n name: this.config.server?.name,\n version: this.config.server?.version,\n description: this.config.description,\n };\n }\n\n /**\n * Generate complete MCP server with stdio transport\n *\n * Creates a runnable Node.js script that exposes SMRT objects as MCP tools.\n * The generated server includes:\n * - Stdio transport integration\n * - Tool registration from ObjectRegistry\n * - Error handling and logging\n * - Graceful shutdown\n *\n * @param options - Server generation options\n * @returns Promise that resolves when all files are written\n *\n * @example\n * ```typescript\n * const generator = new MCPGenerator({\n * name: 'my-app',\n * version: '1.0.0'\n * });\n *\n * await generator.generateServer({\n * outputPath: '.smrt/mcp-server/index.js',\n * serverName: 'my-app-mcp',\n * debug: true\n * });\n * ```\n */\n async generateServer(\n options: {\n /** Path to output server file (relative or absolute) */\n outputPath?: string;\n\n /** Server name for configuration */\n serverName?: string;\n\n /** Server version */\n serverVersion?: string;\n\n /** Enable debug logging */\n debug?: boolean;\n\n /** Generate Claude Desktop configuration example */\n generateClaudeConfigFile?: boolean;\n\n /** Generate README documentation */\n generateReadme?: boolean;\n\n /** Generate modular directory structure (tools/, handlers/, config.ts) */\n modular?: boolean;\n } = {},\n ): Promise<void> {\n const {\n outputPath = '.smrt/mcp-server/index.js',\n serverName = this.config.name || 'smrt-mcp-server',\n serverVersion = this.config.version || '1.0.0',\n debug = false,\n generateClaudeConfigFile = false,\n generateReadme = false,\n modular = false,\n } = options;\n\n // Resolve output path\n const resolvedPath = resolve(process.cwd(), outputPath);\n const outputDir = dirname(resolvedPath);\n\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n if (modular) {\n // Generate modular structure: tools/, handlers/, config.ts, index.js\n await this.generateModularServer(\n outputDir,\n serverName,\n serverVersion,\n debug,\n );\n } else {\n // Generate single-file server with static tools\n const tools = await this.generateTools();\n\n const runtimeOptions: RuntimeOptions = {\n name: serverName,\n version: serverVersion,\n description: this.config.description,\n config: this.config,\n context: this.context,\n debug,\n tools,\n tenantScopedObjects: await this.tenantScopedObjectNames(tools),\n };\n\n const serverCode = generateRuntimeBootstrap(runtimeOptions);\n\n // Write server file\n await writeFile(resolvedPath, serverCode, 'utf-8');\n console.log(`✅ Generated MCP server: ${resolvedPath}`);\n }\n\n // Generate Claude Desktop configuration example\n if (generateClaudeConfigFile) {\n const claudeConfig = generateClaudeConfig(serverName, resolvedPath);\n const claudeConfigPath = resolve(outputDir, 'claude-config.example.json');\n await writeFile(\n claudeConfigPath,\n JSON.stringify(claudeConfig, null, 2),\n 'utf-8',\n );\n console.log(`✅ Generated Claude config example: ${claudeConfigPath}`);\n }\n\n // Generate README documentation\n if (generateReadme) {\n const readme = generateMCPDocumentation(serverName, outputPath);\n const readmePath = resolve(outputDir, 'MCP-README.md');\n await writeFile(readmePath, readme, 'utf-8');\n console.log(`✅ Generated MCP documentation: ${readmePath}`);\n }\n\n // Generate npm script suggestion\n const mcpScript = generateMCPScript(outputPath);\n console.log(`\\n📝 Add this to your package.json scripts:`);\n console.log(` \"mcp\": \"${mcpScript}\"\\n`);\n }\n\n /**\n * Generate modular MCP server structure\n *\n * Creates separate files for tools, handlers, configuration, and main entry point.\n * This makes the generated server easier to customize and extend.\n *\n * @param outputDir - Directory to generate files in\n * @param serverName - Server name\n * @param serverVersion - Server version\n * @param debug - Enable debug logging\n */\n private async generateModularServer(\n outputDir: string,\n serverName: string,\n serverVersion: string,\n debug: boolean,\n ): Promise<void> {\n // Create subdirectories\n const toolsDir = resolve(outputDir, 'tools');\n const handlersDir = resolve(outputDir, 'handlers');\n\n await mkdir(toolsDir, { recursive: true });\n await mkdir(handlersDir, { recursive: true });\n\n // Generate config.ts\n const configPath = resolve(outputDir, 'config.ts');\n const configCode = this.generateConfigFile(\n serverName,\n serverVersion,\n debug,\n );\n await writeFile(configPath, configCode, 'utf-8');\n console.log(`✅ Generated config: ${configPath}`);\n\n // Generate tools/index.ts with tool definitions\n const toolsPath = resolve(toolsDir, 'index.ts');\n const toolsCode = await this.generateToolsFile();\n await writeFile(toolsPath, toolsCode, 'utf-8');\n console.log(`✅ Generated tools: ${toolsPath}`);\n\n // Generate handlers/index.ts with tool call handlers\n const handlersPath = resolve(handlersDir, 'index.ts');\n const tenantScopedObjects = await this.tenantScopedObjectNames(\n await this.generateTools(),\n );\n const handlersCode = await this.generateHandlersFile(tenantScopedObjects);\n await writeFile(handlersPath, handlersCode, 'utf-8');\n console.log(`✅ Generated handlers: ${handlersPath}`);\n\n // Generate main index.js entry point\n const indexPath = resolve(outputDir, 'index.js');\n const indexCode = this.generateModularIndex();\n await writeFile(indexPath, indexCode, 'utf-8');\n console.log(`✅ Generated MCP server: ${indexPath}`);\n }\n\n /**\n * Generate configuration file for modular server\n */\n private generateConfigFile(\n serverName: string,\n serverVersion: string,\n debug: boolean,\n ): string {\n return `/**\n * MCP Server Configuration\n * Auto-generated by @happyvertical/smrt-core\n */\n\nexport const SERVER_NAME = ${JSON.stringify(serverName)};\nexport const SERVER_VERSION = ${JSON.stringify(serverVersion)};\nexport const SERVER_DESCRIPTION = ${JSON.stringify(this.config.description)};\nexport const DEBUG = ${debug};\n`;\n }\n\n /**\n * Generate tools definitions file for modular server\n */\n private async generateToolsFile(): Promise<string> {\n const tools = await this.generateTools();\n\n return `/**\n * MCP Tools Definitions\n * Auto-generated from SMRT objects\n */\n\nexport const tools: Array<{\n name: string;\n description: string;\n inputSchema: any;\n}> = ${JSON.stringify(tools, null, 2)};\n`;\n }\n\n /**\n * Generate switch cases for tool execution\n */\n private async generateToolSwitchCases(\n indent: string = ' ',\n ): Promise<string> {\n const tools = await this.generateTools();\n\n const capitalize = (str: string) =>\n str.charAt(0).toUpperCase() + str.slice(1);\n\n const switchCases = 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 return switchCases;\n }\n\n /**\n * Generate handlers file for modular server\n */\n private async generateHandlersFile(\n tenantScopedObjects: string[] = [],\n ): Promise<string> {\n const switchCases = await this.generateToolSwitchCases(' ');\n const tenantScopedSet = Array.from(\n new Set(tenantScopedObjects.map((n) => n.toLowerCase())),\n );\n const hasTenantScoped = tenantScopedSet.length > 0;\n\n return `/**\n * MCP Tool Call Handlers\n * Auto-generated from SMRT objects\n *\n * SECURITY (#1540): responses exclude @field({ sensitive }) fields and\n * create/update bodies are mass-assignment guarded. This handler has no\n * per-call authentication principal — the generated stdio MCP server's trust\n * boundary is the host process / MCP client. Run it only in a trusted context\n * or behind an authenticated gateway.\n *\n * SECURITY (#1554): tenant-scoped tools run inside a fail-closed tenant gate;\n * the tenant is taken from SMRT_MCP_TENANT_ID (this server has no auth\n * principal) and tenancy is enabled so the interceptor enforces filtering.\n */\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n${hasTenantScoped ? \"import { enableTenancy, runTenantScopedEntryPoint } from '@happyvertical/smrt-tenancy';\\n\" : ''}${\n hasTenantScoped\n ? `\n// Install the tenancy interceptor so tenant-scoped tools are filtered and the\n// gate fail-closes when no tenant is supplied (#1554).\nenableTenancy();\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<string>([\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 as Record<string, any>)) {\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 as Record<string, any>)) {\n out[key] = toPublicResult(entry, seen);\n }\n return out;\n}\n\n/**\n * Handle tool call request\n */\nexport async function handleToolCall(\n name: string,\n arguments: any = {},\n aiConfig: any = {}\n) {\n try {\n const args = arguments;\n\n const runToolBody = async () => {\n switch (name) {\n${switchCases}\n\n default:\n throw new Error(\\`Unknown tool: \\${name}\\`);\n }\n };\n${\n hasTenantScoped\n ? `\n // Fail-closed tenant context for tenant-scoped tools (#1554).\n const [toolObject] = name.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 return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n\n return {\n content: [\n {\n type: 'text',\n text: \\`Error executing tool \\${name}: \\${errorMessage}\\`,\n },\n ],\n isError: true,\n };\n }\n}\n`;\n }\n\n /**\n * Generate modular index file (main entry point)\n */\n private generateModularIndex(): string {\n return `#!/usr/bin/env node\n/**\n * Auto-generated MCP Server\n * Generated by @happyvertical/smrt-core MCPGenerator\n *\n * This server exposes SMRT objects as MCP tools for AI integration.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { config } from '@happyvertical/smrt-config';\nimport { getDatabase } from '@happyvertical/sql';\nimport { getAI } from '@happyvertical/ai';\n\nimport { SERVER_NAME, SERVER_VERSION, DEBUG } from './config.js';\nimport { tools } from './tools/index.js';\nimport { handleToolCall } from './handlers/index.js';\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 console.error(\\`[MCP] Available tools:\\`, tools.map(t => t.name).join(', '));\n }\n\n // Load configuration from environment and .smrt.config files\n const appConfig = await config.load();\n const aiConfig = appConfig?.ai || {};\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 () => {\n if (DEBUG) {\n console.error(\\`[MCP] ListTools request received\\`);\n }\n\n return {\n tools: tools.map(tool => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n })),\n };\n });\n\n // Register CallTool handler\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args = {} } = request.params;\n\n if (DEBUG) {\n console.error(\\`[MCP] CallTool request: \\${name}\\`);\n console.error(\\`[MCP] Arguments:\\`, JSON.stringify(args, null, 2));\n }\n\n return await handleToolCall(name, args, aiConfig);\n });\n\n // Setup stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n if (DEBUG) {\n console.error(\\`[MCP] Server connected and ready\\`);\n }\n\n // Handle graceful shutdown\n const shutdown = async () => {\n if (DEBUG) {\n console.error(\\`[MCP] Shutting down gracefully\\`);\n }\n await server.close();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n } catch (error) {\n console.error('[MCP] Fatal error during server startup:', error);\n process.exit(1);\n }\n}\n\nmain().catch((error) => {\n console.error('[MCP] Unhandled error:', error);\n process.exit(1);\n});\n`;\n }\n}\n"],"names":["collection"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mcp.js","sources":["../../src/generators/mcp.ts"],"sourcesContent":["/**\n * MCP (Model Context Protocol) server generator for smrt objects\n *\n * Exposes smrt objects as AI tools for Claude, GPT, and other AI models\n */\n\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, resolve } from 'node:path';\nimport { SmrtCollection } from '../collection';\nimport type { SmrtObject } from '../object';\nimport { ObjectRegistry } from '../registry';\nimport type { RegisteredClass } from '../registry/types.js';\nimport type { FieldDefinition } from '../scanner/types.js';\nimport {\n generateClaudeConfig,\n generateMCPDocumentation,\n generateMCPScript,\n generateRuntimeBootstrap,\n type RuntimeOptions,\n} from './mcp-runtime-template.js';\nimport { runWithTenantGate } from './tenant-gate.js';\n\n/**\n * A JSON Schema fragment for an MCP tool input property. The shape varies by\n * field kind (string/number/object/…), so values are `unknown`; this is only\n * ever serialized into the generated tool definitions, never read back.\n */\ntype McpJsonSchema = Record<string, unknown>;\n\n/**\n * Runtime tool-call arguments. They arrive as untyped JSON from the MCP client,\n * so individual keys are narrowed (`as`) at each action's boundary.\n */\ntype ToolArgs = Record<string, unknown>;\n\n/**\n * A method resolved dynamically (by action name) from an object/collection\n * instance and invoked with the parsed tool-call arguments. Narrowed to this\n * type at the call boundary after a `typeof === 'function'` guard.\n */\ntype InstanceCallable = (...args: unknown[]) => unknown;\n\nexport interface MCPConfig {\n name?: string;\n version?: string;\n description?: string;\n server?: {\n name: string;\n version: string;\n };\n}\n\nexport interface MCPContext {\n db?: unknown;\n ai?: unknown;\n user?: {\n id: string;\n roles?: string[];\n };\n /**\n * Tenant the calling principal is scoped to (#1554). When set, tenant-scoped\n * tools run inside this tenant's context. Hosts that authenticate a principal\n * (e.g. `@happyvertical/smrt-app-mcp`) should derive it from the principal —\n * the MCP analogue of the SvelteKit auth hook setting `locals.tenantId`.\n */\n tenantId?: string;\n /**\n * Explicit operator opt-in to cross-tenant access for tenant-scoped tools\n * (#1554). Only set for trusted operator/admin callers. Without a `tenantId`\n * or this flag, tenant-scoped tool calls fail closed when tenancy is enabled.\n */\n allowCrossTenant?: boolean;\n}\n\nexport interface MCPTool {\n name: string;\n description: string;\n inputSchema: {\n type: string;\n properties: Record<string, McpJsonSchema>;\n required?: string[];\n };\n}\n\nexport interface MCPRequest {\n method: string;\n params: {\n name: string;\n arguments: ToolArgs;\n };\n}\n\nexport interface MCPResponse {\n content: Array<{\n type: 'text';\n text: string;\n }>;\n}\n\n/**\n * Generate MCP server from smrt objects\n */\nexport class MCPGenerator {\n private config: MCPConfig;\n private context: MCPContext;\n private collections = new Map<string, SmrtCollection<SmrtObject>>();\n\n constructor(config: MCPConfig = {}, context: MCPContext = {}) {\n this.config = {\n name: 'smrt-mcp-server',\n version: '1.0.0',\n description: 'Auto-generated MCP server from smrt objects',\n server: {\n name: 'smrt-mcp',\n version: '1.0.0',\n },\n ...config,\n };\n this.context = context;\n }\n\n /**\n * Get server name\n */\n get name(): string | undefined {\n return this.config.name;\n }\n\n /**\n * Get server version\n */\n get version(): string | undefined {\n return this.config.version;\n }\n\n /**\n * Generate all available tools from registered objects\n */\n async generateTools(): Promise<MCPTool[]> {\n const tools: MCPTool[] = [];\n const registeredClasses = ObjectRegistry.getAllClasses();\n\n for (const [key, classInfo] of registeredClasses) {\n // Issue #951: Use simple name for tool naming, map key for registry lookups\n const simpleName = classInfo.name || key;\n const config = ObjectRegistry.getConfig(simpleName);\n const mcpConfig = config.mcp;\n\n // `mcp: false` disables MCP generation entirely for the class. Without\n // this gate an `include` list still leaked custom-method tools (the\n // custom-method branch historically only honored `include` when it\n // listed custom methods), so `false` is the only fully fail-closed\n // switch. Mirrors rest.ts's `apiConfig === false` short-circuit\n // (#1540 / #1546).\n if (mcpConfig === false) {\n continue;\n }\n\n // Handle boolean vs object config\n const excluded: string[] =\n typeof mcpConfig === 'object' && mcpConfig?.exclude\n ? mcpConfig.exclude\n : [];\n const included: string[] | undefined =\n typeof mcpConfig === 'object' ? mcpConfig?.include : undefined;\n\n const shouldInclude = (endpoint: string) => {\n if (included && !included.includes(endpoint)) return false;\n if (excluded.includes(endpoint)) return false;\n return true;\n };\n\n const objectTools = await this.generateObjectTools(\n simpleName,\n shouldInclude,\n );\n tools.push(...objectTools);\n }\n\n return tools;\n }\n\n /**\n * Generate tools for a specific object\n */\n private async generateObjectTools(\n objectName: string,\n shouldInclude: (endpoint: string) => boolean,\n ): Promise<MCPTool[]> {\n const tools: MCPTool[] = [];\n const fields = ObjectRegistry.getFields(objectName);\n const lowerName = objectName.toLowerCase();\n const classInfo = ObjectRegistry.getClass(objectName);\n\n // LIST tool\n if (shouldInclude('list')) {\n tools.push({\n name: `${lowerName}_list`,\n description: `List ${objectName} objects with optional filtering`,\n inputSchema: {\n type: 'object',\n properties: {\n limit: {\n type: 'integer',\n description: 'Maximum number of items to return',\n default: 50,\n minimum: 1,\n maximum: 1000,\n },\n offset: {\n type: 'integer',\n description: 'Number of items to skip',\n default: 0,\n minimum: 0,\n },\n orderBy: {\n type: 'string',\n description: 'Field to order by (e.g., \"created_at DESC\")',\n },\n where: {\n type: 'object',\n description: 'Filter conditions as key-value pairs',\n additionalProperties: true,\n },\n },\n },\n });\n }\n\n // GET tool\n if (shouldInclude('get')) {\n tools.push({\n name: `${lowerName}_get`,\n description: `Get a specific ${objectName} by ID or slug`,\n inputSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'Unique identifier of the object',\n },\n slug: {\n type: 'string',\n description: 'URL-friendly identifier of the object',\n },\n },\n required: ['id'],\n },\n });\n }\n\n // CREATE tool\n if (shouldInclude('create')) {\n const properties: Record<string, McpJsonSchema> = {};\n const required: string[] = [];\n\n for (const [fieldName, field] of fields) {\n properties[fieldName] = this.fieldToMCPSchema(field);\n if (field._meta?.required) {\n required.push(fieldName);\n }\n }\n\n tools.push({\n name: `${lowerName}_create`,\n description: `Create a new ${objectName}`,\n inputSchema: {\n type: 'object',\n properties,\n required,\n },\n });\n }\n\n // UPDATE tool\n if (shouldInclude('update')) {\n const properties: Record<string, McpJsonSchema> = {\n id: {\n type: 'string',\n description: 'ID of the object to update',\n },\n };\n\n for (const [fieldName, field] of fields) {\n properties[fieldName] = this.fieldToMCPSchema(field);\n }\n\n tools.push({\n name: `${lowerName}_update`,\n description: `Update an existing ${objectName}`,\n inputSchema: {\n type: 'object',\n properties,\n required: ['id'],\n },\n });\n }\n\n // DELETE tool\n if (shouldInclude('delete')) {\n tools.push({\n name: `${lowerName}_delete`,\n description: `Delete a ${objectName} by ID`,\n inputSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'ID of the object to delete',\n },\n },\n required: ['id'],\n },\n });\n }\n\n // CUSTOM METHODS - discover from manifest and show by default\n if (classInfo) {\n const config = ObjectRegistry.getConfig(objectName);\n const mcpConfig = config.mcp;\n const included: string[] | undefined =\n typeof mcpConfig === 'object' ? mcpConfig?.include : undefined;\n const excluded: string[] =\n typeof mcpConfig === 'object' && mcpConfig?.exclude\n ? mcpConfig.exclude\n : [];\n\n // When an `include` list is present it is the COMPLETE allowlist for\n // this surface: a custom (non-CRUD) method is exposed ONLY if its name\n // appears in `include`. Without an include list we keep the historical\n // default of auto-exposing every public method. This closes the leak\n // where `mcp: { include: ['list', 'get'] }` still emitted custom-method\n // tools like `payment_recordpayment` because `include` only gated CRUD\n // verbs (#1540 / #1390).\n const crudOperations = ['list', 'get', 'create', 'update', 'delete'];\n const customMethodsInInclude =\n included?.filter((item) => !crudOperations.includes(item)) || [];\n // An include list (even one naming only CRUD verbs) switches custom\n // methods into strict allowlist mode.\n const hasIncludeList = included !== undefined;\n\n // Try to discover methods from manifest (including inherited methods)\n const methods = await ObjectRegistry.getAllMethods(objectName);\n const methodNames = new Set(Array.from(methods.keys()));\n\n // Strict mode: an include list is present, so only the custom methods it\n // names are generated (may be none, e.g. include: ['list', 'get']).\n if (hasIncludeList) {\n for (const methodName of customMethodsInInclude) {\n // Skip if explicitly excluded\n if (excluded.includes(methodName)) continue;\n\n // Check if method exists (in manifest or on class prototype)\n const existsInManifest = methodNames.has(methodName);\n const existsOnClass = this.validateCustomMethod(\n classInfo.constructor,\n methodName,\n );\n\n if (!existsInManifest && !existsOnClass) {\n // Warn about missing methods\n console.warn(\n `Warning: Custom action '${methodName}' specified in MCP config for ${objectName}, but method ${methodName}() not found on class`,\n );\n continue;\n }\n\n // A non-public method must never be exposed as a tool, even when it\n // is explicitly named in `include`. This keeps strict-include mode\n // consistent with the non-strict path below, which gates on\n // `methodDef.isPublic`. Listing a private method in `include` is a\n // config mistake, not an override of method visibility (#1540).\n //\n // The scanner strips private/protected methods from the manifest, so\n // when a non-public method is named in `include` it is absent from\n // `methods` and is only resolvable via validateCustomMethod() on the\n // runtime prototype (TS access modifiers are erased at runtime). Such\n // a method must NOT be emitted. We still allow methods that are\n // present on the class but legitimately absent from the manifest\n // (e.g. inline/dynamically registered classes), so the guard fires\n // only when there is a public manifest entry to anchor on OR the\n // method exists solely as a stripped (non-public) manifest method.\n const methodDef = methods.get(methodName);\n if (methodDef && !methodDef.isPublic) continue;\n\n // Generate tool for this method\n const toolName = `${lowerName}_${methodName}`.toLowerCase();\n tools.push({\n name: toolName,\n description: `Execute ${methodName} action on ${objectName}`,\n inputSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'ID of the object to execute action on',\n },\n options: {\n type: 'object',\n description: 'Additional options for the custom action',\n additionalProperties: true,\n },\n },\n required: ['id'],\n },\n });\n }\n } else {\n // No custom methods in include = show all discovered methods by default\n for (const [methodName, methodDef] of methods) {\n // Skip if not public (private/protected methods shouldn't be in MCP)\n if (!methodDef.isPublic) continue;\n\n // Always respect exclude list\n if (excluded.includes(methodName)) continue;\n\n // Generate MCP tool for this custom method\n const toolName = `${lowerName}_${methodName}`.toLowerCase();\n tools.push({\n name: toolName,\n description: `Execute ${methodName} action on ${objectName}`,\n inputSchema: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description: 'ID of the object to execute action on',\n },\n options: {\n type: 'object',\n description: 'Additional options for the custom action',\n additionalProperties: true,\n },\n },\n required: ['id'],\n },\n });\n }\n }\n }\n\n return tools;\n }\n\n /**\n * Validate that a custom method exists on a class\n */\n private validateCustomMethod(\n classConstructor: typeof SmrtObject,\n methodName: string,\n ): boolean {\n try {\n // Check if method exists on the prototype\n const prototype = classConstructor.prototype;\n\n // Check if the method exists and is a function. Dynamic name lookup, so\n // index through a record view of the prototype/constructor.\n if (\n typeof (prototype as unknown as Record<string, unknown>)[methodName] ===\n 'function'\n ) {\n return true;\n }\n\n // Also check static methods\n if (\n typeof (classConstructor as unknown as Record<string, unknown>)[\n methodName\n ] === 'function'\n ) {\n return true;\n }\n\n return false;\n } catch (error) {\n console.warn(\n `Error validating method ${methodName} on class ${classConstructor.name}:`,\n error,\n );\n return false;\n }\n }\n\n /**\n * Convert field definition to MCP schema\n */\n private fieldToMCPSchema(field: FieldDefinition): McpJsonSchema {\n const schema: McpJsonSchema = {\n description: field._meta?.description || `${field.type} field`,\n };\n\n switch (field.type) {\n case 'text':\n schema.type = 'string';\n if (field._meta?.maxLength) schema.maxLength = field._meta.maxLength;\n if (field._meta?.minLength) schema.minLength = field._meta.minLength;\n break;\n case 'integer':\n schema.type = 'integer';\n if (field._meta?.min !== undefined) schema.minimum = field._meta.min;\n if (field._meta?.max !== undefined) schema.maximum = field._meta.max;\n break;\n case 'decimal':\n schema.type = 'number';\n if (field._meta?.min !== undefined) schema.minimum = field._meta.min;\n if (field._meta?.max !== undefined) schema.maximum = field._meta.max;\n break;\n case 'boolean':\n schema.type = 'boolean';\n break;\n case 'datetime':\n schema.type = 'string';\n schema.format = 'date-time';\n break;\n case 'json':\n schema.type = 'object';\n break;\n case 'foreignKey':\n schema.type = 'string';\n schema.description = `ID of related ${field.related || 'object'}`;\n break;\n default:\n schema.type = 'string';\n }\n\n if (field._meta?.default !== undefined) {\n schema.default = field._meta.default;\n }\n\n return schema;\n }\n\n /**\n * Handle MCP tool calls\n */\n async handleToolCall(request: MCPRequest): Promise<MCPResponse> {\n const { name, arguments: args } = request.params;\n\n try {\n // Check if tool exists\n const availableTools = await this.generateTools();\n const toolExists = availableTools.some((t) => t.name === name);\n\n if (!toolExists) {\n throw new Error(`Unknown tool: ${name}`);\n }\n\n // Parse tool name: `objectname_action`. Split on the FIRST underscore\n // only — a custom method name can itself contain underscores (e.g.\n // `record_payment` → tool `invoice_record_payment`). A naive\n // `name.split('_')` would take `action` as just `record` and mis-route\n // the call. The emitted stdio servers switch on the full tool name, so\n // splitting greedily here also kept the in-process path divergent (#1378).\n const firstUnderscore = name.indexOf('_');\n const objectName =\n firstUnderscore === -1 ? '' : name.slice(0, firstUnderscore);\n const action =\n firstUnderscore === -1 ? '' : name.slice(firstUnderscore + 1);\n\n if (!objectName || !action) {\n throw new Error(`Invalid tool name format: ${name}`);\n }\n\n // Find the registered class (case-insensitive)\n const registeredClasses = ObjectRegistry.getAllClasses();\n let classInfo = null;\n let actualObjectName = '';\n\n for (const [_key, info] of registeredClasses) {\n // Issue #951: Match by simple name, not the qualified map key\n const simpleName = info.name || _key;\n if (simpleName.toLowerCase() === objectName.toLowerCase()) {\n classInfo = info;\n actualObjectName = simpleName;\n break;\n }\n }\n\n if (!classInfo) {\n throw new Error(`Object type '${objectName}' not found`);\n }\n\n // Get or create collection\n const collection = await this.getCollection(actualObjectName, classInfo);\n\n // Execute the action\n const result = await this.executeAction(\n collection,\n action,\n args,\n actualObjectName,\n );\n\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n } catch (error) {\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,\n },\n ],\n };\n }\n }\n\n /**\n * Get or create collection for an object\n */\n private async getCollection(\n objectName: string,\n classInfo: RegisteredClass,\n ): Promise<SmrtCollection<SmrtObject>> {\n if (!this.collections.has(objectName)) {\n // Ensure we have a valid collection constructor\n if (\n !classInfo.collectionConstructor ||\n typeof classInfo.collectionConstructor !== 'function'\n ) {\n throw new Error(\n `No valid collection constructor found for ${objectName}`,\n );\n }\n\n const collection = new classInfo.collectionConstructor({\n ai: this.context.ai,\n db: this.context.db,\n });\n\n // Verify the collection is actually a SmrtCollection instance\n if (!(collection instanceof SmrtCollection)) {\n throw new Error(\n `Collection for ${objectName} must extend SmrtCollection`,\n );\n }\n\n // Initialize the collection (database setup, etc.)\n await collection.initialize();\n\n this.collections.set(objectName, collection);\n }\n const collection = this.collections.get(objectName);\n if (!collection) {\n throw new Error(`Collection for ${objectName} not found`);\n }\n return collection;\n }\n\n /**\n * Serialize a tool-response payload, excluding sensitive fields (#1540).\n * Recurses through arrays and plain objects so a SmrtObject nested inside a\n * custom-action result (e.g. `{ item }`) is also stripped — `JSON.stringify`\n * would otherwise call its `toJSON()`. Non-plain instances (Date, etc.) and\n * primitives pass through unchanged; 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 const publicSource = value as { toPublicJSON?: () => unknown };\n if (typeof publicSource.toPublicJSON === 'function') {\n return publicSource.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 /**\n * Mass-assignment guard (#1540): strip framework/server-managed and\n * `@field({ readonly: true })` fields from a create/update body, and — when an\n * `@smrt({ api: { writable: [...] } })` allowlist is set — intersect with it.\n */\n private applyWritablePolicy(\n objectName: string | undefined,\n data: unknown,\n ): Record<string, unknown> {\n if (!data || typeof data !== 'object') {\n return {};\n }\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 * Execute action on collection\n */\n /**\n * Fail-closed authorization for tool calls (#1540). Mutating tools\n * (create/update/delete + custom actions) require an authenticated principal\n * (`context.user`) unless the object opts out via `@smrt({ api: { public } })`.\n * Reads are allowed when `public` is `true` or `'read'`.\n */\n private requireToolAuth(\n objectName: string | undefined,\n mutating: boolean,\n ): void {\n const apiConfig = objectName\n ? ObjectRegistry.getConfig(objectName)?.api\n : undefined;\n const publicAccess =\n apiConfig && typeof apiConfig === 'object'\n ? (apiConfig as { public?: boolean | 'read' }).public\n : undefined;\n\n if (publicAccess === true) return;\n if (publicAccess === 'read' && !mutating) return;\n if (!this.context.user) {\n throw new Error('Authentication required');\n }\n }\n\n private async executeAction(\n collection: SmrtCollection<SmrtObject>,\n action: string,\n args: ToolArgs,\n objectName?: string,\n ): Promise<unknown> {\n const mutating = action !== 'list' && action !== 'get';\n this.requireToolAuth(objectName, mutating);\n\n // Fail-closed tenant context (#1554). For tenant-scoped objects, establish\n // the context from the principal's tenant (or an explicit cross-tenant\n // opt-in); without either, this throws when tenancy is enabled rather than\n // letting an optional-scoped read range across all tenants. Tenant-scoping\n // is resolved inside tenancy by class name so it matches the interceptor.\n return runWithTenantGate(\n {\n className: objectName,\n tenantId: this.context.tenantId,\n allowCrossTenant: this.context.allowCrossTenant,\n surface: 'MCP',\n },\n () => this.runAction(collection, action, args, objectName),\n );\n }\n\n /**\n * Derive the set of tenant-scoped object names (lowercased simple names) from\n * a generated tool list, for the emitted runtime template's tenant gate\n * (#1554). Tool names are `objectname_action`.\n *\n * Detection uses ONLY tenancy's `isTenantScopedClass` (the authoritative\n * source the interceptor uses; covers `@TenantScoped`), consulted via an\n * optional dynamic import. We deliberately do NOT fall back to core's\n * `ObjectRegistry.isTenantScoped`: a `@smrt({ tenantScoped })` model can exist\n * in an app that has NOT installed `@happyvertical/smrt-tenancy`, and emitting\n * the static `import { runTenantScopedEntryPoint } from '@happyvertical/smrt-tenancy'`\n * gate for it would crash the generated server at import time. When tenancy is\n * absent there is also nothing to enforce, so emitting no gate is correct.\n */\n private async tenantScopedObjectNames(tools: MCPTool[]): Promise<string[]> {\n let isTenantScopedClass: ((name: string) => boolean) | undefined;\n try {\n // Held in a variable so neither TypeScript's declaration emit (TS2307 —\n // core deliberately does not depend on tenancy) nor the bundler tries to\n // statically resolve this optional sibling package. Same pattern as\n // `tenant-gate.ts` / `embeddings/provider.ts`.\n const tenancySpecifier = '@happyvertical/smrt-tenancy';\n const tenancy = (await import(/* @vite-ignore */ tenancySpecifier)) as {\n isTenantScopedClass?: (name: string) => boolean;\n };\n isTenantScopedClass = tenancy.isTenantScopedClass;\n } catch {\n isTenantScopedClass = undefined;\n }\n\n // No tenancy package installed → no gate can run, emit none.\n if (typeof isTenantScopedClass !== 'function') return [];\n\n const scoped = new Set<string>();\n for (const tool of tools) {\n const [objectName] = tool.name.split('_');\n if (!objectName) continue;\n // Resolve the registered simple name (case-insensitive) and test scoping.\n for (const [key, info] of ObjectRegistry.getAllClasses()) {\n const simpleName = info.name || key;\n if (simpleName.toLowerCase() === objectName.toLowerCase()) {\n if (isTenantScopedClass(simpleName)) {\n scoped.add(simpleName.toLowerCase());\n }\n break;\n }\n }\n }\n return Array.from(scoped);\n }\n\n /**\n * Execute a resolved MCP action (CRUD or custom) against a collection. Always\n * invoked inside the tenant gate established by {@link executeAction}.\n */\n private async runAction(\n collection: SmrtCollection<SmrtObject>,\n action: string,\n args: ToolArgs,\n objectName?: string,\n ): Promise<unknown> {\n switch (action) {\n case 'list': {\n // Args arrive as untyped JSON; narrow each query field at this\n // boundary. `where`/`orderBy` are passed through to the collection's\n // typed query API.\n const listOptions: Parameters<typeof collection.list>[0] = {\n limit: Math.min((args.limit as number | undefined) || 50, 1000),\n offset: (args.offset as number | undefined) || 0,\n };\n\n if (args.where) {\n listOptions.where = args.where as (typeof listOptions)['where'];\n }\n\n if (args.orderBy) {\n listOptions.orderBy = args.orderBy as string | string[];\n }\n\n const results = await collection.list(listOptions);\n const total = await collection.count({\n where: (args.where as (typeof listOptions)['where']) || {},\n });\n\n return {\n data: results.map((result) => this.toPublicData(result)),\n meta: {\n total,\n limit: listOptions.limit,\n offset: listOptions.offset,\n count: results.length,\n },\n };\n }\n\n case 'get': {\n if (!args.id && !args.slug) {\n throw new Error('Either id or slug is required');\n }\n\n const filter = (args.id ? args.id : args.slug) as string;\n const item = await collection.get(filter);\n\n if (!item) {\n throw new Error('Object not found');\n }\n\n return this.toPublicData(item);\n }\n\n case 'create': {\n // Mass-assignment guard (#1540): only writable fields from the caller.\n const createData: Record<string, unknown> = this.applyWritablePolicy(\n objectName,\n args,\n );\n // Server-set ownership context (not caller-controlled).\n if (this.context.user) {\n createData.created_by = this.context.user.id;\n createData.owner_id = this.context.user.id;\n }\n\n // The writable-policy output is a dynamically-shaped record of caller\n // data; cast to the collection's create input at this boundary.\n const newItem = await collection.create(\n createData as Parameters<typeof collection.create>[0],\n );\n await newItem.save();\n\n return this.toPublicData(newItem);\n }\n\n case 'update': {\n const id = args.id as string | undefined;\n if (!id) {\n throw new Error('ID is required for update');\n }\n\n const existing = await collection.get(id);\n if (!existing) {\n throw new Error('Object not found');\n }\n\n // Mass-assignment guard (#1540): strip server-managed/read-only keys\n // (incl. `id`) before applying caller-supplied updates.\n const updateData = this.applyWritablePolicy(objectName, args);\n Object.assign(existing, updateData);\n\n // Add user context. `updated_by` is a server-set audit column, not a\n // declared model field, so assign it through a record view.\n if (this.context.user) {\n (existing as unknown as Record<string, unknown>).updated_by =\n this.context.user.id;\n }\n\n await existing.save();\n\n return this.toPublicData(existing);\n }\n\n case 'delete': {\n if (!args.id) {\n throw new Error('ID is required for delete');\n }\n\n const toDelete = await collection.get(args.id as string);\n if (!toDelete) {\n throw new Error('Object not found');\n }\n\n await toDelete.delete();\n\n return { success: true, message: 'Object deleted successfully' };\n }\n\n default: {\n // Handle custom actions. The method may return a SmrtObject (or array),\n // so serialize through toPublicData to strip sensitive fields (#1540).\n const result = await this.executeCustomAction(collection, action, args);\n return this.toPublicData(result);\n }\n }\n }\n\n /**\n * Execute a custom action on a collection/object\n */\n private async executeCustomAction(\n collection: SmrtCollection<SmrtObject>,\n action: string,\n args: ToolArgs,\n ): Promise<unknown> {\n const { id, options: rawOptions = {}, ...directArgs } = args;\n // Args arrive as untyped JSON; `options` is a nested object bag.\n const options = rawOptions as Record<string, unknown>;\n\n try {\n // If an ID is provided, get the specific object and call the method on it\n if (id) {\n const object = await collection.get(id as string);\n if (!object) {\n throw new Error('Object not found');\n }\n\n // Custom action names are resolved dynamically, so index the instance\n // through a record view and narrow the value to a callable after the\n // `typeof === 'function'` guard.\n const objectWithMethods = object as unknown as Record<string, unknown>;\n const objectMethod = objectWithMethods[action];\n if (typeof objectMethod === 'function') {\n // Call the method with the provided options\n // If options is provided, use it; otherwise use directArgs\n const methodArgs =\n Object.keys(options).length > 0 ? options : directArgs;\n // `.call(object, …)` preserves the receiver binding of the original\n // member call (`object[action](…)`) — the method relies on `this`.\n const result = await (objectMethod as InstanceCallable).call(\n object,\n methodArgs,\n );\n return result;\n } else {\n throw new Error(`Method '${action}' not found on object instance`);\n }\n } else {\n // No ID provided, try to call the method on the collection\n const collectionMethod = (\n collection as unknown as Record<string, unknown>\n )[action];\n if (typeof collectionMethod === 'function') {\n const methodArgs =\n Object.keys(options).length > 0 ? options : directArgs;\n // `.call(collection, …)` preserves the receiver binding of the\n // original member call (`collection[action](…)`).\n const result = await (collectionMethod as InstanceCallable).call(\n collection,\n methodArgs,\n );\n return result;\n } else {\n throw new Error(\n `Method '${action}' not found on collection. For object-specific actions, provide an 'id' parameter.`,\n );\n }\n }\n } catch (error) {\n throw new Error(\n `Failed to execute custom action '${action}': ${error instanceof Error ? error.message : 'Unknown error'}`,\n );\n }\n }\n\n /**\n * Generate MCP server info\n */\n getServerInfo() {\n return {\n name: this.config.server?.name,\n version: this.config.server?.version,\n description: this.config.description,\n };\n }\n\n /**\n * Generate complete MCP server with stdio transport\n *\n * Creates a runnable Node.js script that exposes SMRT objects as MCP tools.\n * The generated server includes:\n * - Stdio transport integration\n * - Tool registration from ObjectRegistry\n * - Error handling and logging\n * - Graceful shutdown\n *\n * @param options - Server generation options\n * @returns Promise that resolves when all files are written\n *\n * @example\n * ```typescript\n * const generator = new MCPGenerator({\n * name: 'my-app',\n * version: '1.0.0'\n * });\n *\n * await generator.generateServer({\n * outputPath: '.smrt/mcp-server/index.js',\n * serverName: 'my-app-mcp',\n * debug: true\n * });\n * ```\n */\n async generateServer(\n options: {\n /** Path to output server file (relative or absolute) */\n outputPath?: string;\n\n /** Server name for configuration */\n serverName?: string;\n\n /** Server version */\n serverVersion?: string;\n\n /** Enable debug logging */\n debug?: boolean;\n\n /** Generate Claude Desktop configuration example */\n generateClaudeConfigFile?: boolean;\n\n /** Generate README documentation */\n generateReadme?: boolean;\n\n /** Generate modular directory structure (tools/, handlers/, config.ts) */\n modular?: boolean;\n } = {},\n ): Promise<void> {\n const {\n outputPath = '.smrt/mcp-server/index.js',\n serverName = this.config.name || 'smrt-mcp-server',\n serverVersion = this.config.version || '1.0.0',\n debug = false,\n generateClaudeConfigFile = false,\n generateReadme = false,\n modular = false,\n } = options;\n\n // Resolve output path\n const resolvedPath = resolve(process.cwd(), outputPath);\n const outputDir = dirname(resolvedPath);\n\n // Ensure output directory exists\n await mkdir(outputDir, { recursive: true });\n\n if (modular) {\n // Generate modular structure: tools/, handlers/, config.ts, index.js\n await this.generateModularServer(\n outputDir,\n serverName,\n serverVersion,\n debug,\n );\n } else {\n // Generate single-file server with static tools\n const tools = await this.generateTools();\n\n const runtimeOptions: RuntimeOptions = {\n name: serverName,\n version: serverVersion,\n description: this.config.description,\n config: this.config,\n context: this.context,\n debug,\n tools,\n tenantScopedObjects: await this.tenantScopedObjectNames(tools),\n };\n\n const serverCode = generateRuntimeBootstrap(runtimeOptions);\n\n // Write server file\n await writeFile(resolvedPath, serverCode, 'utf-8');\n console.log(`✅ Generated MCP server: ${resolvedPath}`);\n }\n\n // Generate Claude Desktop configuration example\n if (generateClaudeConfigFile) {\n const claudeConfig = generateClaudeConfig(serverName, resolvedPath);\n const claudeConfigPath = resolve(outputDir, 'claude-config.example.json');\n await writeFile(\n claudeConfigPath,\n JSON.stringify(claudeConfig, null, 2),\n 'utf-8',\n );\n console.log(`✅ Generated Claude config example: ${claudeConfigPath}`);\n }\n\n // Generate README documentation\n if (generateReadme) {\n const readme = generateMCPDocumentation(serverName, outputPath);\n const readmePath = resolve(outputDir, 'MCP-README.md');\n await writeFile(readmePath, readme, 'utf-8');\n console.log(`✅ Generated MCP documentation: ${readmePath}`);\n }\n\n // Generate npm script suggestion\n const mcpScript = generateMCPScript(outputPath);\n console.log(`\\n📝 Add this to your package.json scripts:`);\n console.log(` \"mcp\": \"${mcpScript}\"\\n`);\n }\n\n /**\n * Generate modular MCP server structure\n *\n * Creates separate files for tools, handlers, configuration, and main entry point.\n * This makes the generated server easier to customize and extend.\n *\n * @param outputDir - Directory to generate files in\n * @param serverName - Server name\n * @param serverVersion - Server version\n * @param debug - Enable debug logging\n */\n private async generateModularServer(\n outputDir: string,\n serverName: string,\n serverVersion: string,\n debug: boolean,\n ): Promise<void> {\n // Create subdirectories\n const toolsDir = resolve(outputDir, 'tools');\n const handlersDir = resolve(outputDir, 'handlers');\n\n await mkdir(toolsDir, { recursive: true });\n await mkdir(handlersDir, { recursive: true });\n\n // Generate config.ts\n const configPath = resolve(outputDir, 'config.ts');\n const configCode = this.generateConfigFile(\n serverName,\n serverVersion,\n debug,\n );\n await writeFile(configPath, configCode, 'utf-8');\n console.log(`✅ Generated config: ${configPath}`);\n\n // Generate tools/index.ts with tool definitions\n const toolsPath = resolve(toolsDir, 'index.ts');\n const toolsCode = await this.generateToolsFile();\n await writeFile(toolsPath, toolsCode, 'utf-8');\n console.log(`✅ Generated tools: ${toolsPath}`);\n\n // Generate handlers/index.ts with tool call handlers\n const handlersPath = resolve(handlersDir, 'index.ts');\n const tenantScopedObjects = await this.tenantScopedObjectNames(\n await this.generateTools(),\n );\n const handlersCode = await this.generateHandlersFile(tenantScopedObjects);\n await writeFile(handlersPath, handlersCode, 'utf-8');\n console.log(`✅ Generated handlers: ${handlersPath}`);\n\n // Generate main index.js entry point\n const indexPath = resolve(outputDir, 'index.js');\n const indexCode = this.generateModularIndex();\n await writeFile(indexPath, indexCode, 'utf-8');\n console.log(`✅ Generated MCP server: ${indexPath}`);\n }\n\n /**\n * Generate configuration file for modular server\n */\n private generateConfigFile(\n serverName: string,\n serverVersion: string,\n debug: boolean,\n ): string {\n return `/**\n * MCP Server Configuration\n * Auto-generated by @happyvertical/smrt-core\n */\n\nexport const SERVER_NAME = ${JSON.stringify(serverName)};\nexport const SERVER_VERSION = ${JSON.stringify(serverVersion)};\nexport const SERVER_DESCRIPTION = ${JSON.stringify(this.config.description)};\nexport const DEBUG = ${debug};\n`;\n }\n\n /**\n * Generate tools definitions file for modular server\n */\n private async generateToolsFile(): Promise<string> {\n const tools = await this.generateTools();\n\n return `/**\n * MCP Tools Definitions\n * Auto-generated from SMRT objects\n */\n\nexport const tools: Array<{\n name: string;\n description: string;\n inputSchema: any;\n}> = ${JSON.stringify(tools, null, 2)};\n`;\n }\n\n /**\n * Generate switch cases for tool execution\n */\n private async generateToolSwitchCases(\n indent: string = ' ',\n ): Promise<string> {\n const tools = await this.generateTools();\n\n const capitalize = (str: string) =>\n str.charAt(0).toUpperCase() + str.slice(1);\n\n const switchCases = 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 return switchCases;\n }\n\n /**\n * Generate handlers file for modular server\n */\n private async generateHandlersFile(\n tenantScopedObjects: string[] = [],\n ): Promise<string> {\n const switchCases = await this.generateToolSwitchCases(' ');\n const tenantScopedSet = Array.from(\n new Set(tenantScopedObjects.map((n) => n.toLowerCase())),\n );\n const hasTenantScoped = tenantScopedSet.length > 0;\n\n return `/**\n * MCP Tool Call Handlers\n * Auto-generated from SMRT objects\n *\n * SECURITY (#1540): responses exclude @field({ sensitive }) fields and\n * create/update bodies are mass-assignment guarded. This handler has no\n * per-call authentication principal — the generated stdio MCP server's trust\n * boundary is the host process / MCP client. Run it only in a trusted context\n * or behind an authenticated gateway.\n *\n * SECURITY (#1554): tenant-scoped tools run inside a fail-closed tenant gate;\n * the tenant is taken from SMRT_MCP_TENANT_ID (this server has no auth\n * principal) and tenancy is enabled so the interceptor enforces filtering.\n */\n\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n${hasTenantScoped ? \"import { enableTenancy, runTenantScopedEntryPoint } from '@happyvertical/smrt-tenancy';\\n\" : ''}${\n hasTenantScoped\n ? `\n// Install the tenancy interceptor so tenant-scoped tools are filtered and the\n// gate fail-closes when no tenant is supplied (#1554).\nenableTenancy();\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<string>([\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 as Record<string, any>)) {\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 as Record<string, any>)) {\n out[key] = toPublicResult(entry, seen);\n }\n return out;\n}\n\n/**\n * Handle tool call request\n */\nexport async function handleToolCall(\n name: string,\n arguments: any = {},\n aiConfig: any = {}\n) {\n try {\n const args = arguments;\n\n const runToolBody = async () => {\n switch (name) {\n${switchCases}\n\n default:\n throw new Error(\\`Unknown tool: \\${name}\\`);\n }\n };\n${\n hasTenantScoped\n ? `\n // Fail-closed tenant context for tenant-scoped tools (#1554).\n const [toolObject] = name.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 return result;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n\n return {\n content: [\n {\n type: 'text',\n text: \\`Error executing tool \\${name}: \\${errorMessage}\\`,\n },\n ],\n isError: true,\n };\n }\n}\n`;\n }\n\n /**\n * Generate modular index file (main entry point)\n */\n private generateModularIndex(): string {\n return `#!/usr/bin/env node\n/**\n * Auto-generated MCP Server\n * Generated by @happyvertical/smrt-core MCPGenerator\n *\n * This server exposes SMRT objects as MCP tools for AI integration.\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { config } from '@happyvertical/smrt-config';\nimport { getDatabase } from '@happyvertical/sql';\nimport { getAI } from '@happyvertical/ai';\n\nimport { SERVER_NAME, SERVER_VERSION, DEBUG } from './config.js';\nimport { tools } from './tools/index.js';\nimport { handleToolCall } from './handlers/index.js';\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 console.error(\\`[MCP] Available tools:\\`, tools.map(t => t.name).join(', '));\n }\n\n // Load configuration from environment and .smrt.config files\n const appConfig = await config.load();\n const aiConfig = appConfig?.ai || {};\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 () => {\n if (DEBUG) {\n console.error(\\`[MCP] ListTools request received\\`);\n }\n\n return {\n tools: tools.map(tool => ({\n name: tool.name,\n description: tool.description,\n inputSchema: tool.inputSchema,\n })),\n };\n });\n\n // Register CallTool handler\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args = {} } = request.params;\n\n if (DEBUG) {\n console.error(\\`[MCP] CallTool request: \\${name}\\`);\n console.error(\\`[MCP] Arguments:\\`, JSON.stringify(args, null, 2));\n }\n\n return await handleToolCall(name, args, aiConfig);\n });\n\n // Setup stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n if (DEBUG) {\n console.error(\\`[MCP] Server connected and ready\\`);\n }\n\n // Handle graceful shutdown\n const shutdown = async () => {\n if (DEBUG) {\n console.error(\\`[MCP] Shutting down gracefully\\`);\n }\n await server.close();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n } catch (error) {\n console.error('[MCP] Fatal error during server startup:', error);\n process.exit(1);\n }\n}\n\nmain().catch((error) => {\n console.error('[MCP] Unhandled error:', error);\n process.exit(1);\n});\n`;\n }\n}\n"],"names":["collection"],"mappings":"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"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { default as http } from 'node:http';
|
|
1
2
|
import { SmrtCollection } from '../collection';
|
|
2
3
|
import { SmrtObject } from '../object';
|
|
3
4
|
export interface APIConfig {
|
|
@@ -15,8 +16,8 @@ export interface APIConfig {
|
|
|
15
16
|
hostname?: string;
|
|
16
17
|
}
|
|
17
18
|
export interface APIContext {
|
|
18
|
-
db?:
|
|
19
|
-
ai?:
|
|
19
|
+
db?: unknown;
|
|
20
|
+
ai?: unknown;
|
|
20
21
|
user?: {
|
|
21
22
|
id: string;
|
|
22
23
|
username?: string;
|
|
@@ -37,12 +38,12 @@ export declare class APIGenerator {
|
|
|
37
38
|
* @param name - URL path segment for the collection (e.g., 'products' for /api/products)
|
|
38
39
|
* @param collection - Pre-initialized SmrtCollection instance
|
|
39
40
|
*/
|
|
40
|
-
registerCollection(name: string, collection: SmrtCollection<
|
|
41
|
+
registerCollection(name: string, collection: SmrtCollection<SmrtObject>): void;
|
|
41
42
|
/**
|
|
42
43
|
* Create Node.js HTTP server with all routes
|
|
43
44
|
*/
|
|
44
45
|
createServer(): {
|
|
45
|
-
server:
|
|
46
|
+
server: http.Server;
|
|
46
47
|
url: string;
|
|
47
48
|
};
|
|
48
49
|
/**
|
|
@@ -161,7 +162,7 @@ export interface RestServerConfig extends APIConfig {
|
|
|
161
162
|
* Create REST server with health checks using Bun
|
|
162
163
|
*/
|
|
163
164
|
export declare function createRestServer(objects: (typeof SmrtObject)[], context?: APIContext, config?: RestServerConfig): {
|
|
164
|
-
server:
|
|
165
|
+
server: http.Server;
|
|
165
166
|
url: string;
|
|
166
167
|
};
|
|
167
168
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/generators/rest.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/generators/rest.ts"],"names":[],"mappings":"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"}
|
package/dist/generators/rest.js
CHANGED
|
@@ -415,7 +415,8 @@ class APIGenerator {
|
|
|
415
415
|
*/
|
|
416
416
|
getCollection(classInfo) {
|
|
417
417
|
if (!this.collections.has(classInfo.name)) {
|
|
418
|
-
const
|
|
418
|
+
const ctor = classInfo.collectionConstructor;
|
|
419
|
+
const collection2 = new ctor({
|
|
419
420
|
ai: this.context.ai,
|
|
420
421
|
db: this.context.db
|
|
421
422
|
});
|
|
@@ -471,7 +472,8 @@ class APIGenerator {
|
|
|
471
472
|
* (#1540). Falls back to the value unchanged for non-SmrtObject payloads.
|
|
472
473
|
*/
|
|
473
474
|
toPublicData(object) {
|
|
474
|
-
|
|
475
|
+
const serializable = object;
|
|
476
|
+
return typeof serializable?.toPublicJSON === "function" ? serializable.toPublicJSON() : object;
|
|
475
477
|
}
|
|
476
478
|
/**
|
|
477
479
|
* Create JSON response with proper headers
|
|
@@ -573,9 +575,10 @@ function startRestServer(objects, context = {}, config = {}) {
|
|
|
573
575
|
const shutdown = () => {
|
|
574
576
|
return new Promise((shutdownResolve) => {
|
|
575
577
|
console.log("🛑 Shutting down server gracefully...");
|
|
576
|
-
server.
|
|
577
|
-
|
|
578
|
-
|
|
578
|
+
server.close(() => {
|
|
579
|
+
console.log("✅ Server shut down complete");
|
|
580
|
+
shutdownResolve();
|
|
581
|
+
});
|
|
579
582
|
});
|
|
580
583
|
};
|
|
581
584
|
process.on("SIGTERM", shutdown);
|