diff --git a/packages/objectql/src/plugin.ts b/packages/objectql/src/plugin.ts index c0e7e3e4c..4fef7aabe 100644 --- a/packages/objectql/src/plugin.ts +++ b/packages/objectql/src/plugin.ts @@ -129,12 +129,16 @@ export class ObjectQLPlugin implements Plugin { // object registered by plugins (e.g., sys_user from plugin-auth). await this.syncRegisteredSchemas(ctx); + // Bridge all SchemaRegistry objects to metadata service + // This ensures AI tools and other IMetadataService consumers can see all objects + await this.bridgeObjectsToMetadataService(ctx); + // Register built-in audit hooks this.registerAuditHooks(ctx); // Register tenant isolation middleware this.registerTenantMiddleware(ctx); - + ctx.logger.info('ObjectQL engine started', { driversRegistered: this.ql?.['drivers']?.size || 0, objectsRegistered: this.ql?.registry?.getAllObjects?.()?.length || 0 @@ -387,12 +391,12 @@ export class ObjectQLPlugin implements Plugin { return; } - // Phase 2: DB hydration + // Phase 2: DB hydration (loads into SchemaRegistry) try { const { loaded, errors } = await protocol.loadMetaFromDb(); if (loaded > 0 || errors > 0) { - ctx.logger.info('Metadata restored from database', { loaded, errors }); + ctx.logger.info('Metadata restored from database to SchemaRegistry', { loaded, errors }); } else { ctx.logger.debug('No persisted metadata found in database'); } @@ -404,6 +408,63 @@ export class ObjectQLPlugin implements Plugin { } } + /** + * Bridge all SchemaRegistry objects to the metadata service. + * + * This ensures objects registered by plugins and loaded from sys_metadata + * are visible to AI tools and other consumers that query IMetadataService. + * + * Runs after both restoreMetadataFromDb() and syncRegisteredSchemas() to + * catch all objects in the SchemaRegistry regardless of their source. + */ + private async bridgeObjectsToMetadataService(ctx: PluginContext): Promise { + try { + const metadataService = ctx.getService('metadata'); + if (!metadataService || typeof metadataService.register !== 'function') { + ctx.logger.debug('Metadata service unavailable for bridging, skipping'); + return; + } + + if (!this.ql?.registry) { + ctx.logger.debug('SchemaRegistry unavailable for bridging, skipping'); + return; + } + + const objects = this.ql.registry.getAllObjects(); + let bridged = 0; + + for (const obj of objects) { + try { + // Check if object is already in metadata service to avoid duplicates + const existing = await metadataService.getObject(obj.name); + if (!existing) { + // Register object that exists in SchemaRegistry but not in metadata service + await metadataService.register('object', obj.name, obj); + bridged++; + } + } catch (e: unknown) { + ctx.logger.debug('Failed to bridge object to metadata service', { + object: obj.name, + error: e instanceof Error ? e.message : String(e), + }); + } + } + + if (bridged > 0) { + ctx.logger.info('Bridged objects from SchemaRegistry to metadata service', { + count: bridged, + total: objects.length + }); + } else { + ctx.logger.debug('No objects needed bridging (all already in metadata service)'); + } + } catch (e: unknown) { + ctx.logger.debug('Failed to bridge objects to metadata service', { + error: e instanceof Error ? e.message : String(e), + }); + } + } + /** * Load metadata from external metadata service into ObjectQL registry * This enables ObjectQL to use file-based or remote metadata