UNPKG

bc-webclient-mcp

Version:

Model Context Protocol (MCP) server for Microsoft Dynamics 365 Business Central via WebUI protocol. Enables AI assistants to interact with BC through the web client protocol, supporting Card, List, and Document pages with full line item support and server

167 lines 5.28 kB
/** * Page Context Cache Service * * Persists pageContexts to disk to survive MCP server restarts. * Allows Claude to reuse previously opened pages across conversations. * * Storage Format: * - Directory: .cache/pageContexts/ * - Files: {pageContextId}.json * - TTL: 1 hour (configurable) * * Example: * ```typescript * const cache = PageContextCache.getInstance(); * * // Save context * await cache.save(pageContextId, { * sessionId, * pageId, * pageType, * logicalForm, * handlers, * ... * }); * * // Load context (auto-cleans expired) * const context = await cache.load(pageContextId); * if (context) { * // Reuse existing page! * } * ``` */ import type { RepeaterColumnDescription } from '../types/mcp-types.js'; import type { PageState } from '../state/page-state.js'; export interface CachedPageContext { sessionId: string; pageId: string; formIds: string[]; openedAt: number; pageType: 'List' | 'Card' | 'Document' | 'Worksheet' | 'Report'; logicalForm: unknown; handlers: unknown[]; pageState?: PageState; expiresAt: number; savedAt: number; } export interface PageContextCacheConfig { cacheDir?: string; ttlMs?: number; cleanupOnStartup?: boolean; } /** * Singleton cache manager for pageContexts. * Persists to JSON files for durability across restarts. */ export declare class PageContextCache { private static instance; private readonly cacheDir; private readonly ttlMs; private initialized; private initializationPromise; private constructor(); /** * Gets singleton instance. */ static getInstance(config?: PageContextCacheConfig): PageContextCache; /** * Initializes cache directory and cleans up expired entries. * Uses Promise-based lock to prevent concurrent initializations. */ initialize(): Promise<void>; /** * Saves a pageContext to disk. * * @param pageContextId - Unique identifier for this page context * @param context - Page context data (without expiresAt/savedAt, will be added) */ save(pageContextId: string, context: Omit<CachedPageContext, 'expiresAt' | 'savedAt'>): Promise<void>; /** * Loads a pageContext from disk. * Returns null if not found or expired. * * @param pageContextId - Unique identifier for this page context * @returns Cached context or null */ load(pageContextId: string): Promise<CachedPageContext | null>; /** * Deletes a pageContext from disk. */ delete(pageContextId: string): Promise<void>; /** * Cleans up expired pageContexts. * Returns number of deleted entries. * NOTE: This method is called during initialization, so it should NOT call ensureInitialized() */ cleanup(): Promise<number>; /** * Lists all cached pageContexts (for debugging). * Returns map of pageContextId → metadata. */ list(): Promise<Map<string, { pageId: string; pageType: string; age: number; ttl: number; }>>; /** * Clears all cached pageContexts (for testing/debugging). */ clear(): Promise<void>; /** * Enriches repeater columns in a cached page context. * * This method progressively enriches column metadata as BC sends 'rcc' messages * during normal operations (read_page_data, execute_action, etc.). * * @param pageContextId - Unique identifier for the page context * @param formId - FormId of the repeater to enrich * @param columns - Column metadata discovered from 'rcc' messages * @returns true if enrichment successful, false if context not found */ enrichRepeaterColumns(pageContextId: string, formId: string, columns: RepeaterColumnDescription[]): Promise<boolean>; /** * Find a repeater control by formId in LogicalForm tree */ private findRepeaterByFormId; /** * Get PageState for a cached page context * * Phase 1: Returns undefined if not yet initialized * Tools should check if PageState exists before using it * * @param pageContextId - Page context identifier * @returns PageState or undefined */ getPageState(pageContextId: string): Promise<PageState | undefined>; /** * Set PageState for a cached page context * * Phase 1: Updates existing context with PageState * Context must already exist (created by save()) * * @param pageContextId - Page context identifier * @param pageState - PageState to save */ setPageState(pageContextId: string, pageState: PageState): Promise<void>; /** * Check if PageState exists for a page context * * @param pageContextId - Page context identifier * @returns true if PageState exists */ hasPageState(pageContextId: string): Promise<boolean>; /** * JSON replacer for serializing PageState Maps * Converts Maps to objects for JSON storage */ private jsonReplacer; /** * JSON reviver for deserializing PageState Maps * Converts stored objects back to Maps */ private jsonReviver; private getFilePath; private ensureInitialized; } //# sourceMappingURL=page-context-cache.d.ts.map