/** * PromptRegistry — Centralized Prompt Registration & Routing * * The single place where all prompt builders are registered and where * incoming `prompts/list` and `prompts/get` requests are routed. * * Mirrors the design of {@link ToolRegistry} with prompt-specific features: * - O(1) routing via Map lookup * - Tag-based filtering for RBAC exposure * - Lifecycle sync via `notifyChanged()` (→ `notifications/prompts/list_changed`) * * @example * ```typescript * import { PromptRegistry, definePrompt } from '@vinkius-core/mcp-fusion'; * * const promptRegistry = new PromptRegistry(); * promptRegistry.register(AuditPrompt); * promptRegistry.register(OnboardingPrompt); * * // Attach alongside tools: * attachToServer(server, { * tools: toolRegistry, * prompts: promptRegistry, * contextFactory: createContext, * }); * * // Lifecycle sync (e.g., after RBAC change): * promptRegistry.notifyChanged(); * ``` * * @see {@link definePrompt} for creating prompt builders * @see {@link PromptBuilder} for the builder contract * * @module */ import { type PromptBuilder, type PromptResult, type PromptInterceptorFn } from './types.js'; import { type CursorMode } from './CursorCodec.js'; /** MCP Prompt definition (for `prompts/list`) */ export interface McpPromptDef { name: string; description?: string; arguments?: Array<{ name: string; description?: string; required?: boolean; }>; } /** Filter options for selective prompt exposure */ export interface PromptFilter { /** Only include prompts that have ALL these tags (AND logic) */ tags?: string[]; /** Only include prompts that have at least ONE of these tags (OR logic) */ anyTag?: string[]; /** Exclude prompts that have ANY of these tags */ exclude?: string[]; } /** Options for configuring pagination in PromptRegistry */ export interface PromptPaginationOptions { /** * Maximum number of prompts to return per page. * Default: 50 */ pageSize?: number; /** * 'signed' (HMAC) or 'encrypted' (AES-GCM). Default: 'signed' */ cursorMode?: CursorMode; /** * 32-byte secret for the cursor codec. If omitted, uses ephemeral keys. */ cursorSecret?: string; } /** * Callback type for sending `notifications/prompts/list_changed`. * Set by ServerAttachment when the registry is attached to a server. */ export type PromptNotificationSink = () => void; export declare class PromptRegistry { private readonly _builders; private readonly _interceptors; private _notificationSink?; private _notifyDebounceTimer; private _defaultHydrationTimeout; private _cursorCodec; private _pageSize; /** * Configure stateless cursor-based pagination for `prompts/list`. * Overrides the default page size of 50. * * @param options Pagination configuration (pageSize, modes, secrets) */ configurePagination(options: PromptPaginationOptions): void; /** * Set a global hydration timeout for ALL prompts in this registry. * * Individual prompts can override with their own `hydrationTimeout`. * If neither is set, no timeout is applied (backward compatible). * * **Enterprise use case**: The platform team sets a 5s global deadline. * Critical prompts like `morning_briefing` set their own 3s deadline. * Simple prompts (no external I/O) inherit the 5s safety net. * * @param ms - Maximum hydration time in milliseconds (must be > 0) * * @example * ```typescript * const promptRegistry = new PromptRegistry(); * promptRegistry.setDefaultHydrationTimeout(5000); // 5s global safety net * ``` */ setDefaultHydrationTimeout(ms: number): void; /** * Register a single prompt builder. * * Validates that the prompt name is unique and triggers * `buildPromptDefinition()` to compile at registration time. * * @param builder - A prompt builder (from `definePrompt()`) * @throws If a prompt with the same name is already registered */ register(builder: PromptBuilder): void; /** * Register multiple prompt builders at once. */ registerAll(...builders: PromptBuilder[]): void; /** * Register a global Prompt Interceptor. * * Interceptors run AFTER the handler produces its `PromptResult` and BEFORE * the result is returned to the MCP client. They can prepend/append messages * to inject compliance headers, tenant context, RBAC constraints, or any * global state that should wrap every prompt. * * **Enterprise use case**: The CISO requires every LLM interaction to include * tenant isolation rules. Register one interceptor — it applies to all 50 prompts. * * Multiple interceptors are supported. They execute in registration order. * * @param interceptor - Callback receiving (ctx, builder, promptMeta) * * @example * ```typescript * promptRegistry.useInterceptor(async (ctx, builder, meta) => { * builder.prependSystem( * `[ENTERPRISE COMPLIANCE LAYER]\n` + * `User Role: ${ctx.user.role} (Tenant ID: ${ctx.tenant.id})\n` + * `Server Time: ${new Date().toISOString()}` * ); * }); * ``` */ useInterceptor(interceptor: PromptInterceptorFn): void; /** * Get all registered MCP Prompt definitions. * * Returns the compiled prompt metadata for `prompts/list`. * @deprecated Use `listPrompts({ filter })` instead for pagination support. */ getAllPrompts(): McpPromptDef[]; /** * Get prompt definitions filtered by tags. * * Uses Set-based lookups for O(1) tag matching. * @deprecated Use `listPrompts({ filter })` instead for pagination support. */ getPrompts(filter: PromptFilter): McpPromptDef[]; /** * Get paginated prompt definitions for `prompts/list`. * * Applies tag filters and decodes stateless cursors to return * the requested slice of prompts, along with a `nextCursor` if more exist. * Memory consumption is strictly O(1) across connections. * * @param request - Configuration containing optional `filter` and `cursor`. * @returns Object with the array of prompts and an optional `nextCursor`. */ listPrompts(request?: { filter?: PromptFilter; cursor?: string; }): Promise<{ prompts: McpPromptDef[]; nextCursor?: string; }>; /** * Route an incoming `prompts/get` request to the correct builder. * * Looks up the builder by name and delegates to its `execute()` method. * Returns an error prompt result if the prompt is not found. * * @param ctx - Application context (from contextFactory) * @param name - Prompt name from the incoming MCP request * @param args - Raw string arguments from the MCP client * @returns The hydrated prompt result */ routeGet(ctx: TContext, name: string, args: Record): Promise; /** * Set the notification sink for `notifications/prompts/list_changed`. * * Called by `ServerAttachment` when attaching the registry to a server. * The sink invokes the MCP SDK's `sendPromptListChanged()` method. * * @internal — not part of the public API */ setNotificationSink(sink: PromptNotificationSink): void; /** * Notify all connected clients that the prompt catalog has changed. * * Sends `notifications/prompts/list_changed` to all connected clients, * causing them to re-fetch `prompts/list` and update their UI. * * **Debounced:** Multiple calls within 100ms are coalesced into a single * notification to prevent storms during bulk registration or RBAC updates. * * Use cases: * - RBAC change: user promoted/demoted → visible prompts change * - SOP update: compliance rules changed → prompt logic updated * - Feature flag: new prompt enabled for beta users * * @example * ```typescript * // In your RBAC webhook handler: * app.post('/webhooks/role-changed', async (req) => { * await db.users.updateRole(req.userId, req.newRole); * promptRegistry.notifyChanged(); // All clients refresh instantly * }); * ``` */ notifyChanged(): void; /** Check if a prompt with the given name is registered. */ has(name: string): boolean; /** Remove all registered prompts, interceptors, and cancel pending timers. */ clear(): void; /** Number of registered prompts. */ get size(): number; } //# sourceMappingURL=PromptRegistry.d.ts.map