import type { ServerCapabilities } from '@frontmcp/protocol'; import { type EntryOwnerRef, type ResourceEntry, type ResourceRecord, type ResourceTemplateRecord, type ResourceType } from '../common'; import type ProviderRegistry from '../provider/provider.registry'; import { RegistryAbstract, type RegistryBuildMapResult } from '../regsitry'; import { type ResourceChangeEvent } from './resource.events'; import { ResourceInstance } from './resource.instance'; import { type IndexedResource, type ResourceExportOptions } from './resource.types'; export default class ResourceRegistry extends RegistryAbstract { /** Who owns this registry (used for provenance). */ owner: EntryOwnerRef; /** Resources truly owned/constructed by THIS registry (with lineage applied) */ private localRows; /** Adopted resource rows from each child registry (references to the same instances) */ private adopted; /** Children registries that we track */ private children; private byQualifiedId; private byName; private byUri; private byUriTemplate; private byOwnerAndName; private byOwner; private version; private emitter; constructor(providers: ProviderRegistry, list: ResourceType[], owner: EntryOwnerRef); protected buildMap(list: ResourceType[]): RegistryBuildMapResult; protected buildGraph(): void; protected initialize(): Promise; /** * Adopt resources from a child registry. Parent runs after children are ready. * We *reference* the child's resource instances; no duplicates are created. */ adoptFromChild(child: ResourceRegistry, _childOwner: EntryOwnerRef): void; /** * Adopt resources directly from a remote app's resources registry. * Remote apps expose resources via proxy entries that forward execution to the remote server. * This also subscribes to updates from the remote app's registry for lazy-loaded resources. */ private adoptResourcesFromRemoteApp; /** * Adopt resources from a local app's child ResourceRegistry instances. * Local apps use the hierarchical registry pattern for resource discovery. */ private adoptResourcesFromLocalApp; /** * Get all static resources (not templates) */ getResources(includeHidden?: boolean): ResourceEntry[]; /** * Get all resource templates */ getResourceTemplates(): ResourceEntry[]; /** * Get inline resources (local only) */ getInlineResources(): ResourceEntry[]; /** * Find a resource by exact URI match */ findByUri(uri: string): ResourceEntry | undefined; /** * Match a URI against template resources and extract parameters */ matchTemplateByUri(uri: string): { instance: ResourceEntry; params: Record; } | undefined; /** * Find a resource by URI - tries exact match first, then template matching */ findResourceForUri(uri: string): { instance: ResourceEntry; params: Record; } | undefined; /** Internal snapshot of effective indexed rows (locals + adopted). */ listAllIndexed(): IndexedResource[]; /** List all instances (locals + adopted). */ listAllInstances(): readonly ResourceEntry[]; /** List instances by owner path (e.g. "app:Portal/plugin:Okta") */ listByOwner(ownerPath: string): readonly ResourceEntry[]; private reindex; /** * Produce unique, MCP-valid exported names. */ exportResolvedNames(opts?: ResourceExportOptions): Array<{ name: string; instance: ResourceEntry; }>; /** Lookup by the exported (resolved) name. */ getExported(name: string, opts?: ResourceExportOptions): ResourceEntry | undefined; subscribe(opts: { immediate?: boolean; filter?: (i: ResourceEntry) => boolean; }, cb: (evt: ResourceChangeEvent) => void): () => void; /** * Signal that a specific resource's content has changed without modifying the resource list. * Emits an 'updated' event that NotificationService uses to notify subscribed sessions. * * @param uri - The URI of the resource whose content changed */ notifyContentChanged(uri: string): void; private bump; /** Build an IndexedResource row */ private makeRow; /** Clone a child row and prepend lineage (with adjacent de-dup to avoid double prefixes). */ private relineage; /** Best-effort provider id used for prefixing. */ private providerIdOf; /** True if this registry (or adopted children) has any resources. */ hasAny(): boolean; /** * Replace all resources owned by the given owner. * Clears local rows, rebuilds from new list, reindexes, and emits 'reset'. * Used by adapter polling to hot-swap resources when specs change. */ replaceAll(list: ResourceType[], owner: EntryOwnerRef): void; /** * Get the MCP capabilities for resources. * These are reported to clients during initialization. * * Issue #407: we always advertise the `resources` capability so the MCP SDK * accepts the always-registered `resources/list` and `resources/templates/list` * handlers (see `createMcpHandlers`). `subscribe` and `listChanged` reflect * whether anything is actually registered. */ getCapabilities(): Partial; /** * Register an existing ResourceEntry instance directly (for remote resources). * This allows pre-constructed resource instances to be added without going through * the standard token-based initialization flow. * * **IMPORTANT: Scope Binding** * The ResourceEntry captures its scope and providers at construction time. * The resource will use the scope from its original `providers` argument, NOT this registry's scope. * * @param resource - The resource instance to register (ResourceInstance or RemoteResourceInstance) * @throws Error if resource is not a valid instance */ registerResourceInstance(resource: ResourceEntry): void; /** * Dynamically register a resource or resource template at runtime. * * Used for system resources like the ui:// template that should be * registered when tools with UI configs exist. * * @param resourceDef - Resource class, function, or template to register */ registerDynamicResource(resourceDef: ResourceType | ResourceRecord | ResourceTemplateRecord): void; } //# sourceMappingURL=resource.registry.d.ts.map