toolception 0.6.1 → 0.6.2

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.
Files changed (59) hide show
  1. package/README.md +1 -1
  2. package/dist/core/DynamicToolManager.d.ts +33 -22
  3. package/dist/core/DynamicToolManager.d.ts.map +1 -1
  4. package/dist/core/ServerOrchestrator.d.ts +33 -25
  5. package/dist/core/ServerOrchestrator.d.ts.map +1 -1
  6. package/dist/core/ToolRegistry.d.ts +5 -3
  7. package/dist/core/ToolRegistry.d.ts.map +1 -1
  8. package/dist/core/core.types.d.ts +29 -0
  9. package/dist/core/core.types.d.ts.map +1 -0
  10. package/dist/http/FastifyTransport.d.ts +49 -41
  11. package/dist/http/FastifyTransport.d.ts.map +1 -1
  12. package/dist/http/{customEndpoints.d.ts → http.types.d.ts} +33 -86
  13. package/dist/http/http.types.d.ts.map +1 -0
  14. package/dist/http/http.utils.d.ts +121 -0
  15. package/dist/http/http.utils.d.ts.map +1 -0
  16. package/dist/index.d.ts +5 -5
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +1087 -850
  19. package/dist/index.js.map +1 -1
  20. package/dist/mode/ModeResolver.d.ts +11 -10
  21. package/dist/mode/ModeResolver.d.ts.map +1 -1
  22. package/dist/mode/ModuleResolver.d.ts +18 -4
  23. package/dist/mode/ModuleResolver.d.ts.map +1 -1
  24. package/dist/mode/mode.types.d.ts +15 -0
  25. package/dist/mode/mode.types.d.ts.map +1 -0
  26. package/dist/permissions/PermissionAwareFastifyTransport.d.ts +15 -39
  27. package/dist/permissions/PermissionAwareFastifyTransport.d.ts.map +1 -1
  28. package/dist/permissions/PermissionResolver.d.ts +9 -24
  29. package/dist/permissions/PermissionResolver.d.ts.map +1 -1
  30. package/dist/permissions/{createPermissionAwareBundle.d.ts → permissions.types.d.ts} +17 -18
  31. package/dist/permissions/permissions.types.d.ts.map +1 -0
  32. package/dist/permissions/permissions.utils.d.ts +31 -0
  33. package/dist/permissions/permissions.utils.d.ts.map +1 -0
  34. package/dist/server/createMcpServer.d.ts +3 -46
  35. package/dist/server/createMcpServer.d.ts.map +1 -1
  36. package/dist/server/createPermissionBasedMcpServer.d.ts +2 -64
  37. package/dist/server/createPermissionBasedMcpServer.d.ts.map +1 -1
  38. package/dist/server/server.types.d.ts +71 -0
  39. package/dist/server/server.types.d.ts.map +1 -0
  40. package/dist/server/server.utils.d.ts +45 -0
  41. package/dist/server/server.utils.d.ts.map +1 -0
  42. package/dist/session/ClientResourceCache.d.ts +8 -27
  43. package/dist/session/ClientResourceCache.d.ts.map +1 -1
  44. package/dist/session/SessionContextResolver.d.ts +21 -72
  45. package/dist/session/SessionContextResolver.d.ts.map +1 -1
  46. package/dist/session/session.types.d.ts +29 -0
  47. package/dist/session/session.types.d.ts.map +1 -0
  48. package/dist/session/{validateSessionContextConfig.d.ts → session.utils.d.ts} +1 -2
  49. package/dist/session/session.utils.d.ts.map +1 -0
  50. package/dist/types/index.d.ts +0 -24
  51. package/dist/types/index.d.ts.map +1 -1
  52. package/package.json +1 -1
  53. package/dist/http/customEndpoints.d.ts.map +0 -1
  54. package/dist/http/endpointRegistration.d.ts +0 -35
  55. package/dist/http/endpointRegistration.d.ts.map +0 -1
  56. package/dist/permissions/createPermissionAwareBundle.d.ts.map +0 -1
  57. package/dist/permissions/validatePermissionConfig.d.ts +0 -9
  58. package/dist/permissions/validatePermissionConfig.d.ts.map +0 -1
  59. package/dist/session/validateSessionContextConfig.d.ts.map +0 -1
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/mode/ModeResolver.ts","../src/mode/ModuleResolver.ts","../src/errors/ToolingError.ts","../src/core/ToolRegistry.ts","../src/core/DynamicToolManager.ts","../src/meta/registerMetaTools.ts","../src/core/ServerOrchestrator.ts","../src/session/ClientResourceCache.ts","../src/http/endpointRegistration.ts","../src/http/FastifyTransport.ts","../src/session/SessionContextResolver.ts","../src/session/validateSessionContextConfig.ts","../src/server/createMcpServer.ts","../src/permissions/validatePermissionConfig.ts","../src/permissions/PermissionResolver.ts","../src/permissions/createPermissionAwareBundle.ts","../src/permissions/PermissionAwareFastifyTransport.ts","../src/server/createPermissionBasedMcpServer.ts","../src/http/customEndpoints.ts"],"sourcesContent":["import type { Mode, ToolSetCatalog } from \"../types/index.js\";\n\ninterface ModeResolverKeys {\n dynamic?: string[]; // keys that, when present/true, enable dynamic mode\n toolsets?: string[]; // keys that carry comma-separated toolsets\n}\n\ninterface ModeResolverOptions {\n keys?: ModeResolverKeys;\n}\n\nconst DEFAULT_KEYS: Required<ModeResolverKeys> = {\n dynamic: [\n \"dynamic-tool-discovery\",\n \"dynamicToolDiscovery\",\n \"DYNAMIC_TOOL_DISCOVERY\",\n ],\n toolsets: [\"tool-sets\", \"toolSets\", \"FMP_TOOL_SETS\"],\n};\n\nexport class ToolsetValidator {\n private readonly keys: Required<ModeResolverKeys>;\n\n constructor(options: ModeResolverOptions = {}) {\n this.keys = {\n dynamic: options.keys?.dynamic ?? DEFAULT_KEYS.dynamic,\n toolsets: options.keys?.toolsets ?? DEFAULT_KEYS.toolsets,\n };\n }\n\n public resolveMode(\n env?: Record<string, string | undefined>,\n args?: Record<string, unknown>\n ): Mode | null {\n // Check args first\n if (this.isDynamicEnabled(args)) return \"DYNAMIC\";\n\n const toolsetsFromArgs = this.getToolsetsString(args);\n if (toolsetsFromArgs) return \"STATIC\";\n\n // Check env next\n if (this.isDynamicEnabled(env)) return \"DYNAMIC\";\n\n const toolsetsFromEnv = this.getToolsetsString(env);\n if (toolsetsFromEnv) return \"STATIC\";\n\n return null; // no override\n }\n\n public parseCommaSeparatedToolSets(\n input: string,\n catalog: ToolSetCatalog\n ): string[] {\n if (!input || typeof input !== \"string\") return [];\n const raw = input\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n\n const valid = new Set(Object.keys(catalog));\n const result: string[] = [];\n for (const name of raw) {\n if (valid.has(name)) result.push(name);\n else\n console.warn(\n `Invalid toolset '${name}' ignored. Available: ${Array.from(\n valid\n ).join(\", \")}`\n );\n }\n return result;\n }\n\n public getModulesForToolSets(\n toolsets: string[],\n catalog: ToolSetCatalog\n ): string[] {\n const modules = new Set<string>();\n for (const name of toolsets) {\n const def = catalog[name];\n if (!def) continue;\n (def.modules || []).forEach((m) => modules.add(m));\n }\n return Array.from(modules);\n }\n\n public validateToolsetName(\n name: unknown,\n catalog: ToolSetCatalog\n ): { isValid: boolean; sanitized?: string; error?: string } {\n if (!name || typeof name !== \"string\") {\n return {\n isValid: false,\n error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${Object.keys(\n catalog\n ).join(\", \")}`,\n };\n }\n const sanitized = name.trim();\n if (sanitized.length === 0) {\n return {\n isValid: false,\n error: `Empty toolset name provided. Available toolsets: ${Object.keys(\n catalog\n ).join(\", \")}`,\n };\n }\n if (!catalog[sanitized]) {\n return {\n isValid: false,\n error: `Toolset '${sanitized}' not found. Available toolsets: ${Object.keys(\n catalog\n ).join(\", \")}`,\n };\n }\n return { isValid: true, sanitized };\n }\n\n /**\n * Validates and retrieves modules for a set of toolsets.\n * Note: A toolset with only direct tools (no modules) is valid and returns an empty modules array.\n * @param toolsetNames - Array of toolset names to validate\n * @param catalog - The toolset catalog to validate against\n * @returns Validation result with modules array if valid\n */\n public validateToolsetModules(\n toolsetNames: string[],\n catalog: ToolSetCatalog\n ): { isValid: boolean; modules?: string[]; error?: string } {\n try {\n // Verify all toolset names exist in catalog first\n for (const name of toolsetNames) {\n if (!catalog[name]) {\n return {\n isValid: false,\n error: `Toolset '${name}' not found in catalog`,\n };\n }\n }\n\n // Get modules - empty array is valid (toolset may have only direct tools)\n const modules = this.getModulesForToolSets(toolsetNames, catalog);\n return { isValid: true, modules };\n } catch (error) {\n return {\n isValid: false,\n error: `Error resolving modules for ${toolsetNames.join(\", \")}: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n };\n }\n }\n\n private isDynamicEnabled(\n source?: Record<string, unknown> | Record<string, string | undefined>\n ): boolean {\n if (!source) return false;\n for (const key of this.keys.dynamic) {\n const value = (source as any)[key];\n if (value === true) return true;\n if (typeof value === \"string\") {\n const v = value.trim().toLowerCase();\n if (v === \"true\") return true;\n }\n }\n return false;\n }\n\n private getToolsetsString(\n source?: Record<string, unknown> | Record<string, string | undefined>\n ): string | undefined {\n if (!source) return undefined;\n for (const key of this.keys.toolsets) {\n const value = (source as any)[key];\n if (typeof value === \"string\" && value.trim().length > 0)\n return value as string;\n }\n return undefined;\n }\n}\n","import type {\n ToolSetCatalog,\n ToolSetDefinition,\n McpToolDefinition,\n ModuleLoader,\n} from \"../types/index.js\";\n\n/**\n * Reserved toolset keys that cannot be used in user catalogs.\n * Must match META_TOOLSET_KEY in src/meta/registerMetaTools.ts\n */\nconst RESERVED_TOOLSET_KEYS = [\"_meta\"];\n\nexport interface ModuleResolverOptions {\n catalog: ToolSetCatalog;\n moduleLoaders?: Record<string, ModuleLoader>;\n}\n\nexport class ModuleResolver {\n private readonly catalog: ToolSetCatalog;\n private readonly moduleLoaders: Record<string, ModuleLoader>;\n\n constructor(options: ModuleResolverOptions) {\n // Validate catalog doesn't use reserved keys\n for (const key of RESERVED_TOOLSET_KEYS) {\n if (key in options.catalog) {\n throw new Error(\n `Toolset key '${key}' is reserved for internal use and cannot be used in the catalog`\n );\n }\n }\n this.catalog = options.catalog;\n this.moduleLoaders = options.moduleLoaders ?? {};\n }\n\n public getAvailableToolsets(): string[] {\n return Object.keys(this.catalog);\n }\n\n public getToolsetDefinition(name: string): ToolSetDefinition | undefined {\n return this.catalog[name];\n }\n\n public validateToolsetName(name: unknown): {\n isValid: boolean;\n sanitized?: string;\n error?: string;\n } {\n if (!name || typeof name !== \"string\") {\n return {\n isValid: false,\n error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${this.getAvailableToolsets().join(\n \", \"\n )}`,\n };\n }\n const sanitized = name.trim();\n if (sanitized.length === 0) {\n return {\n isValid: false,\n error: `Empty toolset name provided. Available toolsets: ${this.getAvailableToolsets().join(\n \", \"\n )}`,\n };\n }\n // Check for reserved keys (defense in depth)\n if (RESERVED_TOOLSET_KEYS.includes(sanitized)) {\n return {\n isValid: false,\n error: `Toolset key '${sanitized}' is reserved for internal use`,\n };\n }\n if (!this.catalog[sanitized]) {\n return {\n isValid: false,\n error: `Toolset '${sanitized}' not found. Available toolsets: ${this.getAvailableToolsets().join(\n \", \"\n )}`,\n };\n }\n return { isValid: true, sanitized };\n }\n\n public async resolveToolsForToolsets(\n toolsets: string[],\n context?: unknown\n ): Promise<McpToolDefinition[]> {\n const collected: McpToolDefinition[] = [];\n for (const name of toolsets) {\n const def = this.catalog[name];\n if (!def) continue;\n if (Array.isArray(def.tools) && def.tools.length > 0) {\n collected.push(...def.tools);\n }\n if (Array.isArray(def.modules) && def.modules.length > 0) {\n for (const modKey of def.modules) {\n const loader = this.moduleLoaders[modKey];\n if (!loader) continue;\n try {\n const loaded = await loader(context);\n if (Array.isArray(loaded) && loaded.length > 0) {\n collected.push(...loaded);\n }\n } catch (err) {\n console.warn(\n `Module loader '${modKey}' failed for toolset '${name}':`,\n err\n );\n }\n }\n }\n }\n return collected;\n }\n}\n","import type { ToolingErrorCode } from \"../types/index.js\";\n\nexport class ToolingError extends Error {\n public readonly code: ToolingErrorCode;\n public readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: ToolingErrorCode,\n details?: Record<string, unknown>,\n _options?: unknown\n ) {\n super(message);\n this.name = \"ToolingError\";\n this.code = code;\n this.details = details;\n }\n}\n","import type { McpToolDefinition } from \"../types/index.js\";\nimport { ToolingError } from \"../errors/ToolingError.js\";\n\nexport interface ToolRegistryOptions {\n namespaceWithToolset?: boolean;\n}\n\nexport class ToolRegistry {\n private readonly options: Required<ToolRegistryOptions>;\n private readonly names = new Set<string>();\n private readonly toolsetToNames = new Map<string, Set<string>>();\n\n constructor(options: ToolRegistryOptions = {}) {\n this.options = {\n namespaceWithToolset: options.namespaceWithToolset ?? true,\n };\n }\n\n public getSafeName(toolsetKey: string, toolName: string): string {\n if (!this.options.namespaceWithToolset) return toolName;\n if (toolName.startsWith(`${toolsetKey}.`)) return toolName;\n return `${toolsetKey}.${toolName}`;\n }\n\n public has(name: string): boolean {\n return this.names.has(name);\n }\n\n public add(name: string): void {\n if (this.names.has(name)) {\n throw new ToolingError(\n `Tool name collision: '${name}' already registered`,\n \"E_TOOL_NAME_CONFLICT\"\n );\n }\n this.names.add(name);\n }\n\n public addForToolset(toolsetKey: string, name: string): void {\n this.add(name);\n const set = this.toolsetToNames.get(toolsetKey) ?? new Set<string>();\n set.add(name);\n this.toolsetToNames.set(toolsetKey, set);\n }\n\n public mapAndValidate(\n toolsetKey: string,\n tools: McpToolDefinition[]\n ): McpToolDefinition[] {\n return tools.map((t) => {\n const safe = this.getSafeName(toolsetKey, t.name);\n if (this.has(safe)) {\n throw new ToolingError(\n `Tool name collision for '${safe}'`,\n \"E_TOOL_NAME_CONFLICT\"\n );\n }\n return { ...t, name: safe };\n });\n }\n\n public list(): string[] {\n return Array.from(this.names);\n }\n\n public listByToolset(): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n for (const [k, v] of this.toolsetToNames.entries()) {\n result[k] = Array.from(v);\n }\n return result;\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type {\n ExposurePolicy,\n McpToolDefinition,\n ToolSetDefinition,\n ToolingErrorCode,\n} from \"../types/index.js\";\nimport { ModuleResolver } from \"../mode/ModuleResolver.js\";\nimport { ToolRegistry } from \"./ToolRegistry.js\";\n\nexport interface DynamicToolManagerOptions {\n server: McpServer;\n resolver: ModuleResolver;\n context?: unknown;\n onToolsListChanged?: () => Promise<void> | void;\n exposurePolicy?: ExposurePolicy;\n toolRegistry?: ToolRegistry;\n}\n\nexport class DynamicToolManager {\n private readonly server: McpServer;\n private readonly resolver: ModuleResolver;\n private readonly context?: unknown;\n private readonly onToolsListChanged?: () => Promise<void> | void;\n private readonly exposurePolicy?: ExposurePolicy;\n private readonly toolRegistry: ToolRegistry;\n\n private readonly activeToolsets = new Set<string>();\n\n constructor(options: DynamicToolManagerOptions) {\n this.server = options.server;\n this.resolver = options.resolver;\n this.context = options.context;\n this.onToolsListChanged = options.onToolsListChanged;\n this.exposurePolicy = options.exposurePolicy;\n this.toolRegistry =\n options.toolRegistry ?? new ToolRegistry({ namespaceWithToolset: true });\n }\n\n /**\n * Sends a tool list change notification if configured.\n * Logs warnings on failure instead of throwing.\n * @returns Promise that resolves when notification is sent (or skipped)\n * @private\n */\n private async notifyToolsChanged(): Promise<void> {\n if (!this.onToolsListChanged) return;\n try {\n await this.onToolsListChanged();\n } catch (err) {\n console.warn(\"Failed to send tool list change notification:\", err);\n }\n }\n\n public getAvailableToolsets(): string[] {\n return this.resolver.getAvailableToolsets();\n }\n\n public getActiveToolsets(): string[] {\n return Array.from(this.activeToolsets);\n }\n\n public getToolsetDefinition(name: string): ToolSetDefinition | undefined {\n return this.resolver.getToolsetDefinition(name);\n }\n\n public isActive(name: string): boolean {\n return this.activeToolsets.has(name);\n }\n\n /**\n * Enables a single toolset by name.\n * Validates the toolset, checks exposure policies, resolves tools, and registers them.\n * @param toolsetName - The name of the toolset to enable\n * @param skipNotification - If true, skips the tool list change notification (for batch operations)\n * @returns Result object with success status and message\n */\n public async enableToolset(\n toolsetName: string,\n skipNotification = false\n ): Promise<{ success: boolean; message: string }> {\n const validation = this.resolver.validateToolsetName(toolsetName);\n if (!validation.isValid || !validation.sanitized) {\n return {\n success: false,\n message: validation.error || \"Unknown validation error\",\n };\n }\n const sanitized = validation.sanitized;\n if (this.activeToolsets.has(sanitized)) {\n return {\n success: false,\n message: `Toolset '${sanitized}' is already enabled.`,\n };\n }\n\n // Check exposure policies BEFORE resolving tools to fail fast\n const policyCheck = this.checkExposurePolicy(sanitized);\n if (!policyCheck.allowed) {\n return { success: false, message: policyCheck.message };\n }\n\n // Track tools registered for this enable operation to allow rollback\n const registeredTools: string[] = [];\n\n try {\n const resolvedTools = await this.resolver.resolveToolsForToolsets(\n [sanitized],\n this.context\n );\n\n // Register all resolved tools (direct + module-derived)\n if (resolvedTools && resolvedTools.length > 0) {\n const mapped = this.toolRegistry.mapAndValidate(\n sanitized,\n resolvedTools\n );\n for (const tool of mapped) {\n this.registerSingleTool(tool, sanitized);\n registeredTools.push(tool.name);\n }\n }\n\n // Track state only after successful registration\n this.activeToolsets.add(sanitized);\n\n // Notify list change (unless skipped for batch operations)\n if (!skipNotification) {\n await this.notifyToolsChanged();\n }\n\n return {\n success: true,\n message: `Toolset '${sanitized}' enabled successfully. Registered ${\n resolvedTools?.length ?? 0\n } tools.`,\n };\n } catch (error) {\n // Note: We cannot unregister tools from MCP server, but we can track the inconsistency\n if (registeredTools.length > 0) {\n console.warn(\n `Partial failure enabling toolset '${sanitized}'. ` +\n `${registeredTools.length} tools were registered but toolset activation failed. ` +\n `Tools remain registered due to MCP limitations: ${registeredTools.join(\", \")}`\n );\n }\n // Don't add to activeToolsets since we failed\n return {\n success: false,\n message: `Failed to enable toolset '${sanitized}': ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n };\n }\n }\n\n /**\n * Checks if a toolset is allowed by the exposure policy.\n * @param toolsetName - The sanitized toolset name to check\n * @returns Object indicating if allowed and reason message if not\n * @private\n */\n private checkExposurePolicy(toolsetName: string): {\n allowed: boolean;\n message: string;\n } {\n if (\n this.exposurePolicy?.allowlist &&\n !this.exposurePolicy.allowlist.includes(toolsetName)\n ) {\n return {\n allowed: false,\n message: `Toolset '${toolsetName}' is not allowed by policy.`,\n };\n }\n if (\n this.exposurePolicy?.denylist &&\n this.exposurePolicy.denylist.includes(toolsetName)\n ) {\n return {\n allowed: false,\n message: `Toolset '${toolsetName}' is denied by policy.`,\n };\n }\n if (this.exposurePolicy?.maxActiveToolsets !== undefined) {\n const next = this.activeToolsets.size + 1;\n if (next > this.exposurePolicy.maxActiveToolsets) {\n this.exposurePolicy.onLimitExceeded?.(\n [toolsetName],\n Array.from(this.activeToolsets)\n );\n return {\n allowed: false,\n message: `Activation exceeds maxActiveToolsets (${this.exposurePolicy.maxActiveToolsets}).`,\n };\n }\n }\n return { allowed: true, message: \"\" };\n }\n\n /**\n * Registers a single tool with the MCP server.\n * @param tool - The tool definition to register\n * @param toolsetKey - The toolset key for tracking\n * @private\n */\n private registerSingleTool(tool: McpToolDefinition, toolsetKey: string): void {\n // Only pass annotations if they exist and are not empty\n const hasAnnotations =\n tool.annotations && Object.keys(tool.annotations).length > 0;\n\n if (hasAnnotations && tool.annotations) {\n this.server.tool(\n tool.name,\n tool.description,\n tool.inputSchema as Parameters<typeof this.server.tool>[2],\n tool.annotations,\n async (args: Record<string, unknown>) => {\n return await tool.handler(args);\n }\n );\n } else {\n // Legacy 4-parameter call when no annotations\n this.server.tool(\n tool.name,\n tool.description,\n tool.inputSchema as Parameters<typeof this.server.tool>[2],\n async (args: Record<string, unknown>) => {\n return await tool.handler(args);\n }\n );\n }\n this.toolRegistry.addForToolset(toolsetKey, tool.name);\n }\n\n /**\n * Disables a toolset by name.\n * Note: Due to MCP limitations, tools remain registered but the toolset is marked inactive.\n * @param toolsetName - The name of the toolset to disable\n * @returns Result object with success status and message\n */\n public async disableToolset(\n toolsetName: string\n ): Promise<{ success: boolean; message: string }> {\n const validation = this.resolver.validateToolsetName(toolsetName);\n if (!validation.isValid || !validation.sanitized) {\n const activeToolsets =\n Array.from(this.activeToolsets).join(\", \") || \"none\";\n const base = validation.error || \"Unknown validation error\";\n return {\n success: false,\n message: `${base} Active toolsets: ${activeToolsets}`,\n };\n }\n const sanitized = validation.sanitized;\n if (!this.activeToolsets.has(sanitized)) {\n return {\n success: false,\n message: `Toolset '${sanitized}' is not currently active. Active toolsets: ${\n Array.from(this.activeToolsets).join(\", \") || \"none\"\n }`,\n };\n }\n\n // State-only disable; no unregistration support in MCP\n this.activeToolsets.delete(sanitized);\n\n await this.notifyToolsChanged();\n\n return {\n success: true,\n message: `Toolset '${sanitized}' disabled successfully. Individual tools remain registered due to MCP limitations.`,\n };\n }\n\n public getStatus() {\n return {\n availableToolsets: this.getAvailableToolsets(),\n activeToolsets: this.getActiveToolsets(),\n registeredModules: [],\n totalToolsets: this.getAvailableToolsets().length,\n activeCount: this.activeToolsets.size,\n tools: this.toolRegistry.list(),\n toolsetToTools: this.toolRegistry.listByToolset(),\n };\n }\n\n /**\n * Enables multiple toolsets in a batch operation.\n * Sends a single notification after all toolsets are processed.\n * @param toolsetNames - Array of toolset names to enable\n * @returns Result object with overall success status and individual results\n */\n public async enableToolsets(toolsetNames: string[]): Promise<{\n success: boolean;\n results: Array<{\n name: string;\n success: boolean;\n message: string;\n code?: ToolingErrorCode;\n }>;\n message: string;\n }> {\n const results: Array<{\n name: string;\n success: boolean;\n message: string;\n code?: ToolingErrorCode;\n }> = [];\n\n // Enable each toolset, skipping individual notifications\n for (const name of toolsetNames) {\n try {\n const res = await this.enableToolset(name, true);\n results.push({ name, ...res });\n } catch (err) {\n results.push({\n name,\n success: false,\n message: err instanceof Error ? err.message : \"Unknown error\",\n code: \"E_INTERNAL\",\n });\n }\n }\n\n const successAll = results.every((r) => r.success);\n const anySuccess = results.some((r) => r.success);\n const message = successAll\n ? \"All toolsets enabled\"\n : anySuccess\n ? \"Some toolsets failed to enable\"\n : \"All toolsets failed to enable\";\n\n // Send a single notification after batch is complete (if any changes occurred)\n if (anySuccess) {\n await this.notifyToolsChanged();\n }\n\n return { success: successAll, results, message };\n }\n\n /**\n * Enables all available toolsets in a batch operation.\n * @returns Result object with overall success status and individual results\n */\n public async enableAllToolsets(): Promise<{\n success: boolean;\n results: Array<{\n name: string;\n success: boolean;\n message: string;\n code?: ToolingErrorCode;\n }>;\n message: string;\n }> {\n const all = this.getAvailableToolsets();\n return this.enableToolsets(all);\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Mode } from \"../types/index.js\";\nimport { z } from \"zod\";\nimport { DynamicToolManager } from \"../core/DynamicToolManager.js\";\nimport { ToolRegistry } from \"../core/ToolRegistry.js\";\n\n/**\n * Reserved toolset key for meta-tools.\n * Meta-tools are registered under this key to enable collision detection\n * and tracking via the ToolRegistry.\n */\nexport const META_TOOLSET_KEY = \"_meta\";\n\n/**\n * Registers meta-tools on the MCP server for toolset management.\n *\n * In DYNAMIC mode, all meta-tools are registered:\n * - enable_toolset, disable_toolset: For runtime toolset management\n * - list_toolsets, describe_toolset: For toolset discovery\n * - list_tools: For listing registered tools\n *\n * In STATIC mode, only list_tools is registered since toolsets are fixed at startup.\n *\n * Meta-tools are registered with the ToolRegistry under the reserved \"_meta\" toolset key\n * to enable collision detection with user-defined tools.\n *\n * @param server - The MCP server to register tools on\n * @param manager - The DynamicToolManager instance\n * @param toolRegistry - The ToolRegistry for collision detection\n * @param options - Configuration options including the mode\n */\nexport function registerMetaTools(\n server: McpServer,\n manager: DynamicToolManager,\n toolRegistry: ToolRegistry,\n options?: { mode?: Exclude<Mode, \"ALL\"> }\n): void {\n const mode = options?.mode ?? \"DYNAMIC\";\n\n // Dynamic-mode only tools: enable/disable toolsets at runtime\n if (mode === \"DYNAMIC\") {\n // Register with ToolRegistry for collision detection before server.tool()\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"enable_toolset\");\n server.tool(\n \"enable_toolset\",\n \"Enable a toolset by name\",\n { name: z.string().describe(\"Toolset name\") },\n { destructiveHint: true, idempotentHint: true },\n async (args: { name: string }) => {\n const result = await manager.enableToolset(args.name);\n return {\n content: [{ type: \"text\", text: JSON.stringify(result) }],\n };\n }\n );\n\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"disable_toolset\");\n server.tool(\n \"disable_toolset\",\n \"Disable a toolset by name (state only)\",\n { name: z.string().describe(\"Toolset name\") },\n { destructiveHint: true, idempotentHint: true },\n async (args: { name: string }) => {\n const result = await manager.disableToolset(args.name);\n return {\n content: [{ type: \"text\", text: JSON.stringify(result) }],\n };\n }\n );\n\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"list_toolsets\");\n server.tool(\n \"list_toolsets\",\n \"List available toolsets with active status and definitions\",\n {},\n { readOnlyHint: true, idempotentHint: true },\n async () => {\n const available = manager.getAvailableToolsets();\n const byToolset = manager.getStatus().toolsetToTools;\n const items = available.map((key) => {\n const def = manager.getToolsetDefinition(key);\n return {\n key,\n active: manager.isActive(key),\n definition: def\n ? {\n name: def.name,\n description: def.description,\n modules: def.modules ?? [],\n decisionCriteria: def.decisionCriteria ?? undefined,\n }\n : null,\n tools: byToolset[key] ?? [],\n };\n });\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ toolsets: items }) },\n ],\n };\n }\n );\n\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"describe_toolset\");\n server.tool(\n \"describe_toolset\",\n \"Describe a toolset with definition, active status and tools\",\n { name: z.string().describe(\"Toolset name\") },\n { readOnlyHint: true, idempotentHint: true },\n async (args: { name: string }) => {\n const def = manager.getToolsetDefinition(args.name);\n const byToolset = manager.getStatus().toolsetToTools;\n if (!def) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: `Unknown toolset '${args.name}'` }),\n },\n ],\n };\n }\n const payload = {\n key: args.name,\n active: manager.isActive(args.name),\n definition: {\n name: def.name,\n description: def.description,\n modules: def.modules ?? [],\n decisionCriteria: def.decisionCriteria ?? undefined,\n },\n tools: byToolset[args.name] ?? [],\n };\n return {\n content: [{ type: \"text\", text: JSON.stringify(payload) }],\n };\n }\n );\n }\n\n // list_tools is available in both modes\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"list_tools\");\n server.tool(\n \"list_tools\",\n \"List currently registered tool names (best effort)\",\n {},\n { readOnlyHint: true, idempotentHint: true },\n async () => {\n const status = manager.getStatus();\n const payload = {\n tools: status.tools,\n toolsetToTools: status.toolsetToTools,\n };\n return {\n content: [{ type: \"text\", text: JSON.stringify(payload) }],\n };\n }\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { ToolsetValidator } from \"../mode/ToolsetValidator.js\";\nimport { ModuleResolver } from \"../mode/ModuleResolver.js\";\nimport { DynamicToolManager } from \"./DynamicToolManager.js\";\nimport { registerMetaTools } from \"../meta/registerMetaTools.js\";\nimport type {\n ExposurePolicy,\n Mode,\n ModuleLoader,\n ToolSetCatalog,\n} from \"../types/index.js\";\nimport { ToolRegistry } from \"./ToolRegistry.js\";\n\nexport interface ServerOrchestratorOptions {\n server: McpServer;\n catalog: ToolSetCatalog;\n moduleLoaders?: Record<string, ModuleLoader>;\n exposurePolicy?: ExposurePolicy;\n context?: unknown;\n notifyToolsListChanged?: () => Promise<void> | void;\n startup?: { mode?: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" };\n registerMetaTools?: boolean;\n}\n\nexport class ServerOrchestrator {\n private readonly mode: Exclude<Mode, \"ALL\">;\n private readonly resolver: ModuleResolver;\n private readonly manager: DynamicToolManager;\n private readonly toolsetValidator: ToolsetValidator;\n private readonly initPromise: Promise<void>;\n private initError: Error | null = null;\n\n constructor(options: ServerOrchestratorOptions) {\n this.toolsetValidator = new ToolsetValidator();\n const startup = options.startup ?? {};\n const resolved = this.resolveStartupConfig(startup, options.catalog);\n this.mode = resolved.mode;\n this.resolver = new ModuleResolver({\n catalog: options.catalog,\n moduleLoaders: options.moduleLoaders,\n });\n const toolRegistry = new ToolRegistry({\n namespaceWithToolset:\n options.exposurePolicy?.namespaceToolsWithSetKey ?? true,\n });\n this.manager = new DynamicToolManager({\n server: options.server,\n resolver: this.resolver,\n context: options.context,\n onToolsListChanged: options.notifyToolsListChanged,\n exposurePolicy: options.exposurePolicy,\n toolRegistry,\n });\n\n // Register meta-tools only if requested (default true)\n if (options.registerMetaTools !== false) {\n registerMetaTools(options.server, this.manager, toolRegistry, { mode: this.mode });\n }\n\n // Startup behavior - store promise for async initialization\n const initial = resolved.toolsets;\n this.initPromise = this.initializeToolsets(initial);\n }\n\n /**\n * Initializes toolsets asynchronously during construction.\n * Stores any errors for later retrieval via ensureReady().\n * @param initial - The toolsets to initialize or \"ALL\"\n * @returns Promise that resolves when initialization is complete\n * @private\n */\n private async initializeToolsets(\n initial: string[] | \"ALL\" | undefined\n ): Promise<void> {\n try {\n if (initial === \"ALL\") {\n await this.manager.enableToolsets(this.resolver.getAvailableToolsets());\n } else if (Array.isArray(initial) && initial.length > 0) {\n await this.manager.enableToolsets(initial);\n }\n } catch (error) {\n this.initError =\n error instanceof Error ? error : new Error(String(error));\n console.error(\"Failed to initialize toolsets:\", this.initError);\n }\n }\n\n /**\n * Waits for the orchestrator to be fully initialized.\n * Call this before using the orchestrator to ensure all toolsets are loaded.\n * @throws {Error} If initialization failed\n */\n public async ensureReady(): Promise<void> {\n await this.initPromise;\n if (this.initError) {\n throw this.initError;\n }\n }\n\n /**\n * Checks if the orchestrator has finished initialization.\n * Does not throw on error - use ensureReady() for that.\n * @returns Promise that resolves to true if ready, false if initialization failed\n */\n public async isReady(): Promise<boolean> {\n await this.initPromise;\n return this.initError === null;\n }\n\n private resolveStartupConfig(\n startup: { mode?: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" },\n catalog: ToolSetCatalog\n ): { mode: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" } {\n // Explicit mode dominates\n if (startup.mode) {\n if (startup.mode === \"DYNAMIC\" && startup.toolsets) {\n console.warn(\"startup.toolsets provided but ignored in DYNAMIC mode\");\n return { mode: \"DYNAMIC\" };\n }\n if (startup.mode === \"STATIC\") {\n if (startup.toolsets === \"ALL\")\n return { mode: \"STATIC\", toolsets: \"ALL\" };\n const names = Array.isArray(startup.toolsets) ? startup.toolsets : [];\n const valid: string[] = [];\n for (const name of names) {\n const { isValid, sanitized, error } =\n this.toolsetValidator.validateToolsetName(name, catalog);\n if (isValid && sanitized) valid.push(sanitized);\n else if (error) console.warn(error);\n }\n if (names.length > 0 && valid.length === 0) {\n throw new Error(\n \"STATIC mode requires valid toolsets or 'ALL'; none were valid\"\n );\n }\n return { mode: \"STATIC\", toolsets: valid };\n }\n return { mode: startup.mode };\n }\n\n // No explicit mode; infer from toolsets\n if (startup.toolsets === \"ALL\") return { mode: \"STATIC\", toolsets: \"ALL\" };\n if (Array.isArray(startup.toolsets) && startup.toolsets.length > 0) {\n const valid: string[] = [];\n for (const name of startup.toolsets) {\n const { isValid, sanitized, error } =\n this.toolsetValidator.validateToolsetName(name, catalog);\n if (isValid && sanitized) valid.push(sanitized);\n else if (error) console.warn(error);\n }\n if (valid.length === 0) {\n throw new Error(\n \"STATIC mode requires valid toolsets or 'ALL'; none were valid\"\n );\n }\n return { mode: \"STATIC\", toolsets: valid };\n }\n\n // Default\n return { mode: \"DYNAMIC\" };\n }\n\n public getMode(): Exclude<Mode, \"ALL\"> {\n return this.mode;\n }\n\n public getManager(): DynamicToolManager {\n return this.manager;\n }\n}\n","export interface ClientResourceCacheOptions<T> {\n maxSize?: number;\n ttlMs?: number; // ms\n pruneIntervalMs?: number;\n /**\n * Optional cleanup callback called when a resource is removed from the cache.\n * Use this to close connections, clean up sessions, etc.\n * @param key - The cache key being removed\n * @param resource - The resource being removed\n */\n onEvict?: (key: string, resource: T) => void | Promise<void>;\n}\n\ninterface Entry<T> {\n resource: T;\n lastAccessed: number;\n}\n\nexport class ClientResourceCache<T> {\n private storage = new Map<string, Entry<T>>();\n private maxSize: number;\n private ttlMs: number;\n private onEvict?: (key: string, resource: T) => void | Promise<void>;\n // Use ReturnType<typeof setInterval> for cross-env typings without NodeJS namespace\n private pruneInterval?: ReturnType<typeof setInterval>;\n\n constructor(options: ClientResourceCacheOptions<T> = {}) {\n this.maxSize = options.maxSize ?? 1000;\n this.ttlMs = options.ttlMs ?? 1000 * 60 * 60;\n this.onEvict = options.onEvict;\n const pruneEvery = options.pruneIntervalMs ?? 1000 * 60 * 10;\n this.pruneInterval = setInterval(() => this.pruneExpired(), pruneEvery);\n }\n\n public getEntryCount(): number {\n return this.storage.size;\n }\n\n public getMaxSize(): number {\n return this.maxSize;\n }\n\n public getTtl(): number {\n return this.ttlMs;\n }\n\n public get(key: string): T | null {\n const entry = this.storage.get(key);\n if (!entry) return null;\n if (Date.now() - entry.lastAccessed > this.ttlMs) {\n this.delete(key);\n return null;\n }\n entry.lastAccessed = Date.now();\n this.storage.delete(key);\n this.storage.set(key, entry);\n return entry.resource;\n }\n\n public set(key: string, resource: T): void {\n if (this.storage.size >= this.maxSize) {\n this.evictLeastRecentlyUsed();\n }\n const newEntry: Entry<T> = { resource, lastAccessed: Date.now() };\n this.storage.set(key, newEntry);\n }\n\n /**\n * Removes an entry from the cache.\n * Calls the onEvict callback if configured.\n * @param key - The key to remove\n */\n public delete(key: string): void {\n const entry = this.storage.get(key);\n if (entry) {\n this.storage.delete(key);\n this.#callEvictCallback(key, entry.resource);\n }\n }\n\n /**\n * Stops the background pruning interval and optionally clears all entries.\n * @param clearEntries - If true, also removes all entries and calls onEvict for each\n */\n public stop(clearEntries = false): void {\n if (this.pruneInterval) {\n clearInterval(this.pruneInterval);\n this.pruneInterval = undefined;\n }\n if (clearEntries) {\n this.clear();\n }\n }\n\n /**\n * Clears all entries from the cache.\n * Calls onEvict for each entry being removed.\n */\n public clear(): void {\n // Collect all entries first to avoid modification during iteration\n const entries = Array.from(this.storage.entries());\n this.storage.clear();\n for (const [key, entry] of entries) {\n this.#callEvictCallback(key, entry.resource);\n }\n }\n\n /**\n * Evicts the least recently used entry from the cache.\n * @private\n */\n private evictLeastRecentlyUsed(): void {\n const lruKey = this.storage.keys().next().value as string | undefined;\n if (lruKey) {\n this.delete(lruKey);\n }\n }\n\n /**\n * Removes all expired entries from the cache.\n * @private\n */\n private pruneExpired(): void {\n const now = Date.now();\n const keysToDelete: string[] = [];\n for (const [key, entry] of this.storage.entries()) {\n if (now - entry.lastAccessed > this.ttlMs) {\n keysToDelete.push(key);\n }\n }\n // Delete after iteration to avoid modification during iteration\n for (const key of keysToDelete) {\n this.delete(key);\n }\n }\n\n /**\n * Safely calls the evict callback, catching and logging any errors.\n * @param key - The key being evicted\n * @param resource - The resource being evicted\n * @private\n */\n #callEvictCallback(key: string, resource: T): void {\n if (!this.onEvict) return;\n try {\n const result = this.onEvict(key, resource);\n // Handle async callbacks but don't await\n if (result instanceof Promise) {\n result.catch((err) => {\n console.warn(`Error in cache eviction callback for key '${key}':`, err);\n });\n }\n } catch (err) {\n console.warn(`Error in cache eviction callback for key '${key}':`, err);\n }\n }\n}\n","import type { FastifyInstance, FastifyRequest, FastifyReply } from \"fastify\";\nimport { z } from \"zod\";\nimport { randomUUID } from \"node:crypto\";\nimport type {\n CustomEndpointDefinition,\n CustomEndpointRequest,\n EndpointErrorResponse,\n} from \"./customEndpoints.js\";\n\n/**\n * Options for registering custom endpoints\n */\nexport interface RegisterCustomEndpointsOptions {\n /**\n * Optional function to extract additional context for each request.\n * Used by permission-aware transport to inject permission data.\n */\n contextExtractor?: (\n req: FastifyRequest\n ) => Promise<Record<string, any>> | Record<string, any>;\n}\n\n/**\n * Registers custom endpoints on a Fastify instance.\n * Handles Zod validation, error responses, and type-safe request mapping.\n *\n * @param app - Fastify instance to register endpoints on\n * @param basePath - Base path for all endpoints (e.g., \"/\" or \"/api\")\n * @param endpoints - Array of custom endpoint definitions\n * @param options - Optional configuration for endpoint registration\n *\n * @example\n * ```typescript\n * registerCustomEndpoints(app, \"/api\", [\n * defineEndpoint({\n * method: \"GET\",\n * path: \"/users\",\n * querySchema: z.object({ limit: z.coerce.number() }),\n * handler: async (req) => ({ users: [] }),\n * }),\n * ]);\n * ```\n */\nexport function registerCustomEndpoints(\n app: FastifyInstance,\n basePath: string,\n endpoints: CustomEndpointDefinition[],\n options?: RegisterCustomEndpointsOptions\n): void {\n // Built-in MCP paths that should not be overridden\n const reservedPaths = [\"/mcp\", \"/healthz\", \"/tools\", \"/.well-known/mcp-config\"];\n\n for (const endpoint of endpoints) {\n const fullPath = `${basePath}${endpoint.path}`;\n\n // Check for path conflicts with built-in endpoints\n const isReserved = reservedPaths.some((reserved) =>\n fullPath.startsWith(`${basePath}${reserved}`)\n );\n\n if (isReserved) {\n console.warn(\n `Custom endpoint ${endpoint.method} ${endpoint.path} conflicts with built-in MCP endpoint. Skipping registration.`\n );\n continue;\n }\n\n // Convert method to lowercase for Fastify\n const method = endpoint.method.toLowerCase() as\n | \"get\"\n | \"post\"\n | \"put\"\n | \"delete\"\n | \"patch\";\n\n // Register the endpoint with Fastify\n app[method](fullPath, async (req: FastifyRequest, reply: FastifyReply) => {\n try {\n // Extract client ID from header or generate anonymous ID\n const clientIdHeader = (req.headers[\"mcp-client-id\"] as string)?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0\n ? clientIdHeader\n : `anon-${randomUUID()}`;\n\n // Validate request body if schema provided\n let body: any = undefined;\n if (endpoint.bodySchema) {\n const bodyResult = endpoint.bodySchema.safeParse(req.body);\n if (!bodyResult.success) {\n return createValidationError(reply, \"body\", bodyResult.error);\n }\n body = bodyResult.data;\n }\n\n // Validate query parameters if schema provided\n let query: any = {};\n if (endpoint.querySchema) {\n const queryResult = endpoint.querySchema.safeParse(req.query);\n if (!queryResult.success) {\n return createValidationError(reply, \"query\", queryResult.error);\n }\n query = queryResult.data;\n }\n\n // Validate path parameters if schema provided\n let params: any = {};\n if (endpoint.paramsSchema) {\n const paramsResult = endpoint.paramsSchema.safeParse(req.params);\n if (!paramsResult.success) {\n return createValidationError(reply, \"params\", paramsResult.error);\n }\n params = paramsResult.data;\n }\n\n // Build request object with validated data\n const customRequest: CustomEndpointRequest = {\n body,\n query,\n params,\n headers: req.headers as Record<string, string | string[] | undefined>,\n clientId,\n };\n\n // Merge additional context if provided (e.g., permissions from contextExtractor)\n if (options?.contextExtractor) {\n const additionalContext = await options.contextExtractor(req);\n Object.assign(customRequest, additionalContext);\n }\n\n // Call the user-defined handler with validated and typed data\n const result = await endpoint.handler(customRequest as any);\n\n // Validate response if schema provided\n if (endpoint.responseSchema) {\n const responseResult = endpoint.responseSchema.safeParse(result);\n if (!responseResult.success) {\n // Log the validation error for debugging\n console.error(\n `Response validation failed for ${endpoint.method} ${endpoint.path}:`,\n responseResult.error\n );\n\n // Return generic error to prevent information leakage\n reply.code(500);\n return {\n error: {\n code: \"RESPONSE_VALIDATION_ERROR\",\n message: \"Internal server error: invalid response format\",\n },\n } as EndpointErrorResponse;\n }\n // Return validated response data\n return responseResult.data;\n }\n\n // No response validation - return result as-is\n return result;\n } catch (error) {\n // Handle any errors thrown by the handler\n console.error(\n `Error in custom endpoint ${endpoint.method} ${endpoint.path}:`,\n error\n );\n\n reply.code(500);\n return {\n error: {\n code: \"INTERNAL_ERROR\",\n message:\n error instanceof Error ? error.message : \"Internal server error\",\n },\n } as EndpointErrorResponse;\n }\n });\n }\n}\n\n/**\n * Creates a standardized validation error response.\n * Returns 400 status code with detailed Zod validation errors.\n *\n * @param reply - Fastify reply object\n * @param field - The field that failed validation (body, query, or params)\n * @param error - Zod validation error\n * @returns Formatted error response\n * @private\n */\nfunction createValidationError(\n reply: FastifyReply,\n field: string,\n error: z.ZodError\n): EndpointErrorResponse {\n reply.code(400);\n return {\n error: {\n code: \"VALIDATION_ERROR\",\n message: `Validation failed for ${field}`,\n details: error.errors,\n },\n };\n}\n","import Fastify, {\n type FastifyInstance,\n type FastifyReply,\n type FastifyRequest,\n} from \"fastify\";\nimport cors from \"@fastify/cors\";\nimport { randomUUID } from \"node:crypto\";\nimport type { DynamicToolManager } from \"../core/DynamicToolManager.js\";\nimport type { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport { ClientResourceCache } from \"../session/ClientResourceCache.js\";\nimport type { SessionContextResolver } from \"../session/SessionContextResolver.js\";\nimport type { SessionRequestContext } from \"../types/index.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CustomEndpointDefinition } from \"./customEndpoints.js\";\nimport { registerCustomEndpoints } from \"./endpointRegistration.js\";\n\nexport interface FastifyTransportOptions {\n host?: string;\n port?: number;\n basePath?: string; // e.g. \"/\" or \"/api\"\n cors?: boolean;\n logger?: boolean;\n // Optional DI: provide a Fastify instance (e.g., for tests). If provided, start() will not listen.\n app?: FastifyInstance;\n /**\n * Optional custom HTTP endpoints to register alongside MCP protocol endpoints.\n * Allows adding REST-like endpoints with Zod validation and type inference.\n */\n customEndpoints?: CustomEndpointDefinition[];\n}\n\n/**\n * Callback type for creating a server bundle.\n * Accepts an optional merged context for per-session context support.\n */\nexport type CreateBundleCallback = (mergedContext?: unknown) => {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n};\n\nexport class FastifyTransport {\n private readonly options: {\n host: string;\n port: number;\n basePath: string;\n cors: boolean;\n logger: boolean;\n app?: FastifyInstance;\n customEndpoints?: CustomEndpointDefinition[];\n };\n private readonly defaultManager: DynamicToolManager;\n private readonly createBundle: CreateBundleCallback;\n private readonly sessionContextResolver?: SessionContextResolver;\n private readonly baseContext?: unknown;\n private app: FastifyInstance | null = null;\n private readonly configSchema?: object;\n\n // Per-client server bundles and per-client session transports\n private readonly clientCache = new ClientResourceCache<{\n server: McpServer;\n orchestrator: ServerOrchestrator;\n sessions: Map<string, StreamableHTTPServerTransport>;\n }>({\n onEvict: (_key, bundle) => {\n // Clean up all sessions when a client bundle is evicted\n this.cleanupBundle(bundle);\n },\n });\n\n constructor(\n defaultManager: DynamicToolManager,\n createBundle: CreateBundleCallback,\n options: FastifyTransportOptions = {},\n configSchema?: object,\n sessionContextResolver?: SessionContextResolver,\n baseContext?: unknown\n ) {\n this.defaultManager = defaultManager;\n this.createBundle = createBundle;\n this.sessionContextResolver = sessionContextResolver;\n this.baseContext = baseContext;\n this.options = {\n host: options.host ?? \"0.0.0.0\",\n port: options.port ?? 3000,\n basePath: options.basePath ?? \"/\",\n cors: options.cors ?? true,\n logger: options.logger ?? false,\n app: options.app,\n customEndpoints: options.customEndpoints,\n };\n this.configSchema = configSchema;\n }\n\n public async start(): Promise<void> {\n if (this.app) return;\n const app = this.options.app ?? Fastify({ logger: this.options.logger });\n if (this.options.cors) {\n await app.register(cors, { origin: true });\n }\n\n const base = this.options.basePath.endsWith(\"/\")\n ? this.options.basePath.slice(0, -1)\n : this.options.basePath;\n\n app.get(`${base}/healthz`, async () => ({ ok: true }));\n\n app.get(`${base}/tools`, async () => this.defaultManager.getStatus());\n\n // Config discovery (placeholder schema)\n app.get(`${base}/.well-known/mcp-config`, async (_req, reply) => {\n reply.header(\"Content-Type\", \"application/schema+json; charset=utf-8\");\n const baseSchema = this.configSchema ?? {\n $schema: \"https://json-schema.org/draft/2020-12/schema\",\n title: \"MCP Session Configuration\",\n description: \"Schema for the /mcp endpoint configuration\",\n type: \"object\",\n properties: {},\n required: [],\n \"x-mcp-version\": \"1.0\",\n \"x-query-style\": \"dot+bracket\",\n };\n return baseSchema;\n });\n\n // POST /mcp - JSON-RPC\n app.post(\n `${base}/mcp`,\n async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0\n ? clientIdHeader\n : `anon-${randomUUID()}`;\n\n // When anon id, avoid caching (one-off)\n const useCache = !clientId.startsWith(\"anon-\");\n\n // Build session request context and resolve merged context\n const { cacheKey, mergedContext } = this.resolveSessionContext(\n req,\n clientId\n );\n\n let bundle = useCache ? this.clientCache.get(cacheKey) : null;\n if (!bundle) {\n const created = this.createBundle(mergedContext);\n bundle = {\n server: created.server,\n orchestrator: created.orchestrator,\n sessions: new Map(),\n };\n if (useCache) this.clientCache.set(cacheKey, bundle);\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n\n let transport: StreamableHTTPServerTransport | undefined;\n if (sessionId && bundle.sessions.get(sessionId)) {\n transport = bundle.sessions.get(sessionId)!;\n } else if (!sessionId && isInitializeRequest((req as any).body)) {\n const newSessionId = randomUUID();\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => newSessionId,\n onsessioninitialized: (sid: string) => {\n bundle!.sessions.set(sid, transport!);\n },\n });\n try {\n await bundle.server.connect(transport);\n } catch (error) {\n reply.code(500);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Error initializing server.\" },\n id: null,\n };\n }\n transport.onclose = () => {\n if (transport?.sessionId)\n bundle!.sessions.delete(transport.sessionId);\n };\n } else {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Session not found or expired\" },\n id: null,\n };\n }\n\n // Delegate handling to SDK transport using raw Node req/res\n await transport.handleRequest(\n (req as any).raw,\n (reply as any).raw,\n (req as any).body\n );\n // Fastify will consider the response already sent by transport\n return reply;\n }\n );\n\n // GET /mcp - SSE notifications\n app.get(`${base}/mcp`, async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0 ? clientIdHeader : \"\";\n if (!clientId) {\n reply.code(400);\n return \"Missing mcp-client-id\";\n }\n const bundle = this.clientCache.get(clientId);\n if (!bundle) {\n reply.code(400);\n return \"Invalid or expired client\";\n }\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId) {\n reply.code(400);\n return \"Missing mcp-session-id\";\n }\n const transport = bundle.sessions.get(sessionId);\n if (!transport) {\n reply.code(400);\n return \"Invalid or expired session ID\";\n }\n await transport.handleRequest((req as any).raw, (reply as any).raw);\n return reply;\n });\n\n // DELETE /mcp - terminate session\n app.delete(\n `${base}/mcp`,\n async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0 ? clientIdHeader : \"\";\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!clientId || !sessionId) {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32600,\n message: \"Missing mcp-client-id or mcp-session-id header\",\n },\n id: null,\n };\n }\n const bundle = this.clientCache.get(clientId);\n const transport = bundle?.sessions.get(sessionId);\n if (!bundle || !transport) {\n reply.code(404);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Session not found or expired\" },\n id: null,\n };\n }\n try {\n // Best-effort close and evict\n if (typeof (transport as any).close === \"function\") {\n try {\n await (transport as any).close();\n } catch {}\n }\n } finally {\n if (transport?.sessionId) bundle.sessions.delete(transport.sessionId);\n else bundle.sessions.delete(sessionId);\n }\n reply.code(204).send();\n return reply;\n }\n );\n\n // Register custom endpoints if provided\n // IMPORTANT: Only register if customEndpoints is provided AND has items\n if (this.options.customEndpoints && this.options.customEndpoints.length > 0) {\n registerCustomEndpoints(app, base, this.options.customEndpoints);\n }\n\n // Only listen if we created the app\n if (!this.options.app) {\n await app.listen({ host: this.options.host, port: this.options.port });\n }\n this.app = app;\n }\n\n /**\n * Stops the Fastify server and cleans up all resources.\n * Closes all client sessions and clears the cache.\n */\n public async stop(): Promise<void> {\n if (!this.app) return;\n\n // Stop the cache pruning interval and clear all entries (triggers cleanup)\n this.clientCache.stop(true);\n\n if (!this.options.app) {\n await this.app.close();\n }\n this.app = null;\n }\n\n /**\n * Cleans up resources associated with a client bundle.\n * Closes all sessions within the bundle.\n * @param bundle - The client bundle to clean up\n * @private\n */\n private cleanupBundle(bundle: {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n sessions: Map<string, StreamableHTTPServerTransport>;\n }): void {\n for (const [sessionId, transport] of bundle.sessions.entries()) {\n try {\n if (typeof (transport as any).close === \"function\") {\n (transport as any).close().catch((err: unknown) => {\n console.warn(`Error closing session ${sessionId}:`, err);\n });\n }\n } catch (err) {\n console.warn(`Error closing session ${sessionId}:`, err);\n }\n }\n bundle.sessions.clear();\n }\n\n /**\n * Resolves the session context and generates a cache key for the request.\n * If a session context resolver is configured, it extracts query parameters\n * and merges session-specific context with the base context.\n *\n * @param req - The Fastify request\n * @param clientId - The client identifier\n * @returns Object with cache key and merged context\n * @private\n */\n private resolveSessionContext(\n req: FastifyRequest,\n clientId: string\n ): { cacheKey: string; mergedContext: unknown } {\n // If no session context resolver, use simple clientId cache key\n if (!this.sessionContextResolver) {\n return {\n cacheKey: clientId,\n mergedContext: this.baseContext,\n };\n }\n\n // Build session request context\n const sessionRequestContext: SessionRequestContext = {\n clientId,\n headers: this.extractHeaders(req),\n query: this.extractQuery(req),\n };\n\n // Resolve the merged context\n const result = this.sessionContextResolver.resolve(\n sessionRequestContext,\n this.baseContext\n );\n\n // Build cache key: clientId:suffix\n const cacheKey =\n result.cacheKeySuffix === \"default\"\n ? clientId\n : `${clientId}:${result.cacheKeySuffix}`;\n\n return {\n cacheKey,\n mergedContext: result.context,\n };\n }\n\n /**\n * Extracts headers from a Fastify request as a Record.\n * Normalizes header names to lowercase.\n *\n * @param req - The Fastify request\n * @returns Headers as a string record\n * @private\n */\n private extractHeaders(req: FastifyRequest): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers[key.toLowerCase()] = value;\n } else if (Array.isArray(value) && value.length > 0) {\n headers[key.toLowerCase()] = value[0];\n }\n }\n return headers;\n }\n\n /**\n * Extracts query parameters from a Fastify request as a Record.\n *\n * @param req - The Fastify request\n * @returns Query parameters as a string record\n * @private\n */\n private extractQuery(req: FastifyRequest): Record<string, string> {\n const query: Record<string, string> = {};\n const rawQuery = req.query as Record<string, unknown>;\n if (rawQuery && typeof rawQuery === \"object\") {\n for (const [key, value] of Object.entries(rawQuery)) {\n if (typeof value === \"string\") {\n query[key] = value;\n }\n }\n }\n return query;\n }\n}\n","import type {\n SessionContextConfig,\n SessionRequestContext,\n} from \"../types/index.js\";\nimport { createHash } from \"node:crypto\";\n\n/**\n * Result of session context resolution including the merged context\n * and a cache key suffix for differentiating sessions with different configs.\n */\nexport interface SessionContextResult {\n /**\n * The merged context to pass to module loaders.\n */\n context: unknown;\n\n /**\n * A deterministic hash suffix based on the session config values.\n * Used to differentiate cache entries: `${clientId}:${cacheKeySuffix}`\n * Returns 'default' when no session config is present.\n */\n cacheKeySuffix: string;\n}\n\n/**\n * Resolves per-session context from request query parameters and merges\n * it with the base server context.\n *\n * Features:\n * - Parses query parameter (base64 or JSON encoded)\n * - Filters allowed keys (whitelist enforcement)\n * - Merges session context with base context (shallow or deep)\n * - Generates cache key suffix for session differentiation\n *\n * Security considerations:\n * - Always specify allowedKeys to whitelist permitted session config keys\n * - Invalid encoding silently returns empty session config (fail secure)\n * - Disallowed keys are filtered without logging (prevents info leakage)\n *\n * @example\n * ```typescript\n * const resolver = new SessionContextResolver({\n * enabled: true,\n * queryParam: {\n * name: 'config',\n * encoding: 'base64',\n * allowedKeys: ['API_TOKEN', 'USER_ID'],\n * },\n * merge: 'shallow',\n * });\n *\n * const result = resolver.resolve(\n * { clientId: 'client-1', headers: {}, query: { config: 'eyJBUElfVE9LRU4iOiJ0b2tlbiJ9' } },\n * { baseValue: 'foo' }\n * );\n * // result.context = { baseValue: 'foo', API_TOKEN: 'token' }\n * // result.cacheKeySuffix = 'abc123...'\n * ```\n */\nexport class SessionContextResolver {\n private readonly config: SessionContextConfig;\n private readonly queryParamName: string;\n private readonly encoding: \"base64\" | \"json\";\n private readonly allowedKeys: Set<string> | null;\n private readonly mergeStrategy: \"shallow\" | \"deep\";\n\n constructor(config: SessionContextConfig) {\n this.config = config;\n this.queryParamName = config.queryParam?.name ?? \"config\";\n this.encoding = config.queryParam?.encoding ?? \"base64\";\n this.allowedKeys = config.queryParam?.allowedKeys\n ? new Set(config.queryParam.allowedKeys)\n : null;\n this.mergeStrategy = config.merge ?? \"shallow\";\n }\n\n /**\n * Resolves the session context for a request.\n *\n * @param request - The request context (clientId, headers, query)\n * @param baseContext - The base context from server configuration\n * @returns The resolved context and cache key suffix\n */\n resolve(\n request: SessionRequestContext,\n baseContext: unknown\n ): SessionContextResult {\n // If disabled, return base context with default cache key\n if (this.config.enabled === false) {\n return {\n context: baseContext,\n cacheKeySuffix: \"default\",\n };\n }\n\n // Parse and filter the query parameter config\n const parsedConfig = this.parseQueryConfig(request.query);\n\n // If custom resolver is provided, use it\n if (this.config.contextResolver) {\n try {\n const resolvedContext = this.config.contextResolver(\n request,\n baseContext,\n parsedConfig\n );\n return {\n context: resolvedContext,\n cacheKeySuffix: this.generateCacheKeySuffix(parsedConfig),\n };\n } catch {\n // Fail secure: return base context on resolver error\n return {\n context: baseContext,\n cacheKeySuffix: \"default\",\n };\n }\n }\n\n // Default merge behavior\n const mergedContext = this.mergeContexts(baseContext, parsedConfig);\n return {\n context: mergedContext,\n cacheKeySuffix: this.generateCacheKeySuffix(parsedConfig),\n };\n }\n\n /**\n * Parses the session config from query parameters.\n * Returns empty object on parse failure (fail secure).\n *\n * @param query - Query parameters from the request\n * @returns Parsed and filtered config object\n * @private\n */\n private parseQueryConfig(\n query: Record<string, string>\n ): Record<string, unknown> {\n const rawValue = query[this.queryParamName];\n if (!rawValue) {\n return {};\n }\n\n try {\n let jsonString: string;\n\n if (this.encoding === \"base64\") {\n // Decode base64 to JSON string\n jsonString = Buffer.from(rawValue, \"base64\").toString(\"utf-8\");\n } else {\n // JSON encoding - value should already be JSON string\n jsonString = rawValue;\n }\n\n const parsed = JSON.parse(jsonString);\n\n // Must be an object\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return {};\n }\n\n // Filter allowed keys if whitelist is configured\n return this.filterAllowedKeys(parsed);\n } catch {\n // Fail secure: return empty object on any parse error\n return {};\n }\n }\n\n /**\n * Filters the parsed config to only include allowed keys.\n * If no allowedKeys whitelist is configured, returns the full object.\n *\n * @param parsed - The parsed config object\n * @returns Filtered config with only allowed keys\n * @private\n */\n private filterAllowedKeys(\n parsed: Record<string, unknown>\n ): Record<string, unknown> {\n if (!this.allowedKeys) {\n return parsed;\n }\n\n const filtered: Record<string, unknown> = {};\n for (const key of this.allowedKeys) {\n if (key in parsed) {\n filtered[key] = parsed[key];\n }\n }\n return filtered;\n }\n\n /**\n * Merges the base context with the session config.\n *\n * @param baseContext - The base context from server configuration\n * @param sessionConfig - The parsed session config\n * @returns Merged context\n * @private\n */\n private mergeContexts(\n baseContext: unknown,\n sessionConfig: Record<string, unknown>\n ): unknown {\n // If no session config, return base context as-is\n if (Object.keys(sessionConfig).length === 0) {\n return baseContext;\n }\n\n // If base context is not an object, session config takes precedence\n if (\n typeof baseContext !== \"object\" ||\n baseContext === null ||\n Array.isArray(baseContext)\n ) {\n return sessionConfig;\n }\n\n if (this.mergeStrategy === \"deep\") {\n return this.deepMerge(\n baseContext as Record<string, unknown>,\n sessionConfig\n );\n }\n\n // Shallow merge: session config overrides base context\n return {\n ...(baseContext as Record<string, unknown>),\n ...sessionConfig,\n };\n }\n\n /**\n * Performs a deep merge of two objects.\n * Session config values override base context values.\n *\n * @param base - The base object\n * @param override - The override object\n * @returns Deep merged object\n * @private\n */\n private deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>\n ): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base };\n\n for (const [key, value] of Object.entries(override)) {\n const baseValue = result[key];\n\n if (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n typeof baseValue === \"object\" &&\n baseValue !== null &&\n !Array.isArray(baseValue)\n ) {\n // Both are objects - deep merge\n result[key] = this.deepMerge(\n baseValue as Record<string, unknown>,\n value as Record<string, unknown>\n );\n } else {\n // Override base value\n result[key] = value;\n }\n }\n\n return result;\n }\n\n /**\n * Generates a deterministic cache key suffix based on the session config.\n * Returns 'default' when no session config is present.\n *\n * @param sessionConfig - The parsed session config\n * @returns Hash string or 'default'\n * @private\n */\n private generateCacheKeySuffix(\n sessionConfig: Record<string, unknown>\n ): string {\n if (Object.keys(sessionConfig).length === 0) {\n return \"default\";\n }\n\n // Sort keys for deterministic hash\n const sortedKeys = Object.keys(sessionConfig).sort();\n const normalizedObj: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n normalizedObj[key] = sessionConfig[key];\n }\n\n const jsonString = JSON.stringify(normalizedObj);\n return createHash(\"sha256\").update(jsonString).digest(\"hex\").slice(0, 16);\n }\n}\n","import type { SessionContextConfig } from \"../types/index.js\";\n\n/**\n * Validates a session context configuration object to ensure it meets all requirements.\n * Throws descriptive errors for any validation failures.\n *\n * @param config - The session context configuration to validate\n * @throws {Error} If the configuration is invalid or has incorrect types\n */\nexport function validateSessionContextConfig(config: SessionContextConfig): void {\n validateConfigExists(config);\n validateEnabledField(config);\n validateQueryParamConfig(config);\n validateContextResolver(config);\n validateMergeStrategy(config);\n}\n\n/**\n * Validates that the configuration object exists and is an object.\n *\n * @param config - The session context configuration to validate\n * @throws {Error} If config is null, undefined, or not an object\n * @private\n */\nfunction validateConfigExists(config: SessionContextConfig): void {\n if (!config || typeof config !== \"object\") {\n throw new Error(\n \"Session context configuration must be an object\"\n );\n }\n}\n\n/**\n * Validates the enabled field if provided.\n *\n * @param config - The session context configuration to validate\n * @throws {Error} If enabled is not a boolean\n * @private\n */\nfunction validateEnabledField(config: SessionContextConfig): void {\n if (config.enabled === undefined) {\n return;\n }\n\n if (typeof config.enabled !== \"boolean\") {\n throw new Error(\n `enabled must be a boolean, got ${typeof config.enabled}`\n );\n }\n}\n\n/**\n * Validates the queryParam configuration if provided.\n *\n * @param config - The session context configuration to validate\n * @throws {Error} If queryParam fields have invalid types or values\n * @private\n */\nfunction validateQueryParamConfig(config: SessionContextConfig): void {\n if (config.queryParam === undefined) {\n return;\n }\n\n if (typeof config.queryParam !== \"object\" || config.queryParam === null) {\n throw new Error(\"queryParam must be an object\");\n }\n\n // Validate name\n if (config.queryParam.name !== undefined) {\n if (\n typeof config.queryParam.name !== \"string\" ||\n config.queryParam.name.length === 0\n ) {\n throw new Error(\"queryParam.name must be a non-empty string\");\n }\n }\n\n // Validate encoding\n if (config.queryParam.encoding !== undefined) {\n if (\n config.queryParam.encoding !== \"base64\" &&\n config.queryParam.encoding !== \"json\"\n ) {\n throw new Error(\n `Invalid queryParam.encoding: \"${config.queryParam.encoding}\". Must be \"base64\" or \"json\"`\n );\n }\n }\n\n // Validate allowedKeys\n if (config.queryParam.allowedKeys !== undefined) {\n if (!Array.isArray(config.queryParam.allowedKeys)) {\n throw new Error(\"queryParam.allowedKeys must be an array of strings\");\n }\n\n for (let i = 0; i < config.queryParam.allowedKeys.length; i++) {\n const key = config.queryParam.allowedKeys[i];\n if (typeof key !== \"string\" || key.length === 0) {\n throw new Error(\n `queryParam.allowedKeys[${i}] must be a non-empty string`\n );\n }\n }\n }\n}\n\n/**\n * Validates the contextResolver if provided.\n *\n * @param config - The session context configuration to validate\n * @throws {Error} If contextResolver is not a function\n * @private\n */\nfunction validateContextResolver(config: SessionContextConfig): void {\n if (config.contextResolver === undefined) {\n return;\n }\n\n if (typeof config.contextResolver !== \"function\") {\n throw new Error(\n \"contextResolver must be a function: (request, baseContext, parsedQueryConfig?) => unknown\"\n );\n }\n}\n\n/**\n * Validates the merge strategy if provided.\n *\n * @param config - The session context configuration to validate\n * @throws {Error} If merge is not 'shallow' or 'deep'\n * @private\n */\nfunction validateMergeStrategy(config: SessionContextConfig): void {\n if (config.merge === undefined) {\n return;\n }\n\n if (config.merge !== \"shallow\" && config.merge !== \"deep\") {\n throw new Error(\n `Invalid merge strategy: \"${config.merge}\". Must be \"shallow\" or \"deep\"`\n );\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type {\n ExposurePolicy,\n Mode,\n ModuleLoader,\n SessionContextConfig,\n ToolSetCatalog,\n} from \"../types/index.js\";\nimport { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport {\n FastifyTransport,\n type FastifyTransportOptions,\n} from \"../http/FastifyTransport.js\";\nimport { SessionContextResolver } from \"../session/SessionContextResolver.js\";\nimport { validateSessionContextConfig } from \"../session/validateSessionContextConfig.js\";\nimport { z } from \"zod\";\n\nexport interface CreateMcpServerOptions {\n catalog: ToolSetCatalog;\n moduleLoaders?: Record<string, ModuleLoader>;\n exposurePolicy?: ExposurePolicy;\n context?: unknown;\n startup?: { mode?: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" };\n registerMetaTools?: boolean;\n http?: FastifyTransportOptions;\n /**\n * Factory to create an MCP server instance. Required.\n * In DYNAMIC mode, a new instance is created per client bundle.\n * In STATIC mode, a single instance is created and reused across bundles.\n */\n createServer: () => McpServer;\n configSchema?: object;\n /**\n * Optional per-session context configuration.\n * Enables extracting context from query parameters and merging with base context\n * on a per-request basis. Useful for multi-tenant scenarios.\n *\n * @example\n * ```typescript\n * sessionContext: {\n * enabled: true,\n * queryParam: {\n * name: 'config',\n * encoding: 'base64',\n * allowedKeys: ['API_TOKEN', 'USER_ID'],\n * },\n * merge: 'shallow',\n * }\n * ```\n */\n sessionContext?: SessionContextConfig;\n}\n\n/**\n * Zod schema for validating startup configuration.\n * Uses strict mode to reject unknown properties like 'initialToolsets'.\n */\nconst startupConfigSchema = z\n .object({\n mode: z.enum([\"DYNAMIC\", \"STATIC\"]).optional(),\n toolsets: z.union([z.array(z.string()), z.literal(\"ALL\")]).optional(),\n })\n .strict();\n\nexport async function createMcpServer(options: CreateMcpServerOptions) {\n // Validate startup configuration if provided\n if (options.startup) {\n try {\n startupConfigSchema.parse(options.startup);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const formatted = error.format();\n throw new Error(\n `Invalid startup configuration:\\n${JSON.stringify(formatted, null, 2)}\\n\\n` +\n `Hint: Common mistake - use \"toolsets\" not \"initialToolsets\"`\n );\n }\n throw error;\n }\n }\n\n const mode: Exclude<Mode, \"ALL\"> = options.startup?.mode ?? \"DYNAMIC\";\n\n // Validate session context configuration if provided\n let sessionContextResolver: SessionContextResolver | undefined;\n if (options.sessionContext) {\n validateSessionContextConfig(options.sessionContext);\n sessionContextResolver = new SessionContextResolver(options.sessionContext);\n\n // Warn if sessionContext is used with STATIC mode (limited effect)\n if (mode === \"STATIC\" && options.sessionContext.enabled !== false) {\n console.warn(\n \"sessionContext has limited effect in STATIC mode: all clients share the same server instance with base context. \" +\n \"Use DYNAMIC mode for per-session context isolation.\"\n );\n }\n }\n if (typeof options.createServer !== \"function\") {\n throw new Error(\"createMcpServer: `createServer` (factory) is required\");\n }\n const baseServer: McpServer = options.createServer();\n\n // Typed, guarded notifier\n type NotifierA = {\n server: { notification: (msg: { method: string }) => Promise<void> | void };\n };\n type NotifierB = { notifyToolsListChanged: () => Promise<void> | void };\n const hasNotifierA = (s: unknown): s is NotifierA =>\n typeof (s as NotifierA)?.server?.notification === \"function\";\n const hasNotifierB = (s: unknown): s is NotifierB =>\n typeof (s as NotifierB)?.notifyToolsListChanged === \"function\";\n\n /**\n * Sends a tools list changed notification to the client.\n * Logs warnings on failure instead of throwing.\n * Suppresses \"Not connected\" errors as they're expected when no clients are connected.\n * @param target - The MCP server instance\n */\n const notifyToolsChanged = async (target: unknown) => {\n try {\n if (hasNotifierA(target)) {\n await target.server.notification({\n method: \"notifications/tools/list_changed\",\n });\n return;\n }\n if (hasNotifierB(target)) {\n await target.notifyToolsListChanged();\n }\n } catch (err) {\n // Suppress \"Not connected\" errors - expected when no clients are connected\n const errorMessage = err instanceof Error ? err.message : String(err);\n if (errorMessage === \"Not connected\") {\n return; // Silently ignore - no clients to notify\n }\n // Log other errors as they indicate actual problems\n console.warn(\"Failed to send tools list changed notification:\", err);\n }\n };\n\n const orchestrator = new ServerOrchestrator({\n server: baseServer,\n catalog: options.catalog,\n moduleLoaders: options.moduleLoaders,\n exposurePolicy: options.exposurePolicy,\n context: options.context,\n notifyToolsListChanged: async () => notifyToolsChanged(baseServer),\n startup: options.startup,\n registerMetaTools:\n options.registerMetaTools !== undefined\n ? options.registerMetaTools\n : mode === \"DYNAMIC\",\n });\n\n // In STATIC mode, wait for initialization to complete before starting\n if (mode === \"STATIC\") {\n await orchestrator.ensureReady();\n }\n\n const transport = new FastifyTransport(\n orchestrator.getManager(),\n (mergedContext?: unknown) => {\n // Create a server + orchestrator bundle\n // for a new client when needed\n // Use merged context if provided (from session context resolver),\n // otherwise fall back to base context\n const effectiveContext = mergedContext ?? options.context;\n\n if (mode === \"STATIC\") {\n // Reuse the base server and orchestrator to avoid duplicate registrations\n return { server: baseServer, orchestrator };\n }\n const createdServer: McpServer = options.createServer();\n const createdOrchestrator = new ServerOrchestrator({\n server: createdServer,\n catalog: options.catalog,\n moduleLoaders: options.moduleLoaders,\n exposurePolicy: options.exposurePolicy,\n context: effectiveContext,\n notifyToolsListChanged: async () => notifyToolsChanged(createdServer),\n startup: options.startup,\n registerMetaTools:\n options.registerMetaTools !== undefined\n ? options.registerMetaTools\n : mode === \"DYNAMIC\",\n });\n return { server: createdServer, orchestrator: createdOrchestrator };\n },\n options.http,\n options.configSchema,\n sessionContextResolver,\n options.context\n );\n\n return {\n server: baseServer,\n start: async () => {\n await transport.start();\n },\n close: async () => {\n await transport.stop();\n },\n };\n}\n","import type { PermissionConfig } from \"../types/index.js\";\n\n/**\n * Validates a permission configuration object to ensure it meets all requirements.\n * Throws descriptive errors for any validation failures.\n * @param config - The permission configuration to validate\n * @throws {Error} If the configuration is invalid or missing required fields\n */\nexport function validatePermissionConfig(config: PermissionConfig): void {\n validateConfigExists(config);\n validateSourceField(config);\n validateConfigBasedPermissions(config);\n validateTypes(config);\n}\n\n/**\n * Validates that the configuration object exists.\n * @param config - The permission configuration to validate\n * @throws {Error} If config is null, undefined, or not an object\n * @private\n */\nfunction validateConfigExists(config: PermissionConfig): void {\n if (!config || typeof config !== \"object\") {\n throw new Error(\n \"Permission configuration is required for createPermissionBasedMcpServer\"\n );\n }\n}\n\n/**\n * Validates that the source field is present and has a valid value.\n * @param config - The permission configuration to validate\n * @throws {Error} If source is missing or not 'headers' or 'config'\n * @private\n */\nfunction validateSourceField(config: PermissionConfig): void {\n if (!config.source) {\n throw new Error('Permission source must be either \"headers\" or \"config\"');\n }\n\n if (config.source !== \"headers\" && config.source !== \"config\") {\n throw new Error(\n `Invalid permission source: \"${config.source}\". Must be either \"headers\" or \"config\"`\n );\n }\n}\n\n/**\n * Validates config-based permission requirements.\n * When source is 'config', at least one of staticMap or resolver must be provided.\n * @param config - The permission configuration to validate\n * @throws {Error} If config source is used but neither staticMap nor resolver is provided\n * @private\n */\nfunction validateConfigBasedPermissions(config: PermissionConfig): void {\n if (config.source === \"config\") {\n if (!config.staticMap && !config.resolver) {\n throw new Error(\n \"Config-based permissions require at least one of: staticMap or resolver function\"\n );\n }\n }\n}\n\n/**\n * Validates the types of configuration fields.\n * Ensures staticMap is an object and resolver is a function when provided.\n * @param config - The permission configuration to validate\n * @throws {Error} If staticMap or resolver have incorrect types\n * @private\n */\nfunction validateTypes(config: PermissionConfig): void {\n if (config.staticMap !== undefined) {\n if (typeof config.staticMap !== \"object\" || config.staticMap === null) {\n throw new Error(\n \"staticMap must be an object mapping client IDs to toolset arrays\"\n );\n }\n\n // Validate that staticMap values are arrays\n validateStaticMapValues(config.staticMap);\n }\n\n if (config.resolver !== undefined) {\n if (typeof config.resolver !== \"function\") {\n throw new Error(\n \"resolver must be a synchronous function: (clientId: string) => string[]\"\n );\n }\n }\n\n if (config.defaultPermissions !== undefined) {\n if (!Array.isArray(config.defaultPermissions)) {\n throw new Error(\"defaultPermissions must be an array of toolset names\");\n }\n }\n\n if (config.headerName !== undefined) {\n if (typeof config.headerName !== \"string\" || config.headerName.length === 0) {\n throw new Error(\"headerName must be a non-empty string\");\n }\n }\n}\n\n/**\n * Validates that all values in the staticMap are arrays.\n * @param staticMap - The static map to validate\n * @throws {Error} If any value in the staticMap is not an array\n * @private\n */\nfunction validateStaticMapValues(staticMap: Record<string, string[]>): void {\n for (const [clientId, permissions] of Object.entries(staticMap)) {\n if (!Array.isArray(permissions)) {\n throw new Error(\n `staticMap value for client \"${clientId}\" must be an array of toolset names`\n );\n }\n }\n}\n","import type { PermissionConfig } from \"../types/index.js\";\n\n/**\n * Resolves and caches client permissions based on configured permission sources.\n * Supports both header-based and config-based permission resolution with caching\n * for performance optimization.\n */\nexport class PermissionResolver {\n private cache = new Map<string, string[]>();\n private readonly normalizedHeaderName: string;\n\n /**\n * Creates a new PermissionResolver instance.\n * @param config - The permission configuration defining how permissions are resolved\n */\n constructor(private config: PermissionConfig) {\n // Pre-normalize header name to lowercase for case-insensitive matching\n this.normalizedHeaderName = (\n config.headerName || \"mcp-toolset-permissions\"\n ).toLowerCase();\n }\n\n /**\n * Resolves permissions for a client based on the configured source.\n * Results are cached to improve performance for subsequent requests from the same client.\n * Handles all errors gracefully by returning empty permissions on failure.\n * \n * Note on caching: For header-based permissions, permissions are cached by clientId.\n * This means subsequent requests from the same client will use cached permissions,\n * even if headers change. Use invalidateCache(clientId) to force re-resolution.\n * \n * @param clientId - The unique identifier for the client\n * @param headers - Optional request headers (required for header-based permissions)\n * @returns Array of toolset names the client is allowed to access\n */\n resolvePermissions(\n clientId: string,\n headers?: Record<string, string>\n ): string[] {\n // Check cache first for performance\n if (this.cache.has(clientId)) {\n return this.cache.get(clientId)!;\n }\n\n let permissions: string[];\n\n try {\n if (this.config.source === \"headers\") {\n permissions = this.#parseHeaderPermissions(headers);\n } else {\n permissions = this.#resolveConfigPermissions(clientId);\n }\n\n // Validate that permissions is an array\n if (!Array.isArray(permissions)) {\n console.warn(\n `Permission resolution returned non-array for client ${clientId}, using empty permissions`\n );\n permissions = [];\n }\n\n // Filter out invalid toolset names (empty strings, non-strings)\n permissions = permissions.filter(\n (name) => typeof name === \"string\" && name.trim().length > 0\n );\n } catch (error) {\n // Catch any unexpected errors and apply most restrictive permissions\n console.error(\n `Unexpected error resolving permissions for client ${clientId}:`,\n error\n );\n permissions = [];\n }\n\n // Cache the resolved permissions\n this.cache.set(clientId, permissions);\n return permissions;\n }\n\n /**\n * Invalidates cached permissions for a specific client.\n * Call this when you know a client's permissions have changed.\n * @param clientId - The client ID to invalidate\n */\n invalidateCache(clientId: string): void {\n this.cache.delete(clientId);\n }\n\n /**\n * Parses permissions from request headers.\n * Extracts comma-separated toolset names from the configured header.\n * Handles malformed headers gracefully by returning empty permissions.\n * Uses case-insensitive header lookup per RFC 7230.\n * @param headers - Request headers containing permission data\n * @returns Array of toolset names from headers, or empty array if header is missing/malformed\n * @private\n */\n #parseHeaderPermissions(headers?: Record<string, string>): string[] {\n if (!headers) {\n return [];\n }\n\n // Find header value using case-insensitive lookup\n const headerValue = this.#findHeaderCaseInsensitive(\n headers,\n this.normalizedHeaderName\n );\n\n if (!headerValue) {\n return [];\n }\n\n try {\n // Parse comma-separated list, trim whitespace, and filter empty strings\n return headerValue\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n } catch (error) {\n // Handle malformed headers gracefully\n console.warn(\n `Failed to parse permission header '${this.normalizedHeaderName}':`,\n error\n );\n return [];\n }\n }\n\n /**\n * Finds a header value using case-insensitive key matching.\n * HTTP headers are case-insensitive per RFC 7230.\n * @param headers - The headers object to search\n * @param normalizedKey - The lowercase key to search for\n * @returns The header value if found, undefined otherwise\n * @private\n */\n #findHeaderCaseInsensitive(\n headers: Record<string, string>,\n normalizedKey: string\n ): string | undefined {\n // Fast path: check if key exists as-is (common case with Fastify's lowercased headers)\n if (headers[normalizedKey] !== undefined) {\n return headers[normalizedKey];\n }\n // Slow path: iterate and compare lowercase\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === normalizedKey) {\n return value;\n }\n }\n return undefined;\n }\n\n /**\n * Resolves permissions from server-side configuration.\n * Tries resolver function first (if provided), then falls back to static map,\n * and finally to default permissions. Handles errors gracefully.\n * @param clientId - The unique identifier for the client\n * @returns Array of toolset names from configuration\n * @private\n */\n #resolveConfigPermissions(clientId: string): string[] {\n // Try resolver function first (if provided)\n if (this.config.resolver) {\n const resolverResult = this.#tryResolverFunction(clientId);\n if (resolverResult !== null) {\n return resolverResult;\n }\n // Fall through to static map or default if resolver fails\n }\n\n // Fall back to static map (if provided)\n if (this.config.staticMap) {\n const staticResult = this.#lookupStaticMap(clientId);\n if (staticResult !== null) {\n return staticResult;\n }\n }\n\n // Final fallback to default permissions\n return this.config.defaultPermissions || [];\n }\n\n /**\n * Attempts to resolve permissions using the configured resolver function.\n * Handles errors gracefully and returns null on failure to allow fallback.\n * @param clientId - The unique identifier for the client\n * @returns Array of toolset names if successful, null if resolver fails or returns invalid data\n * @private\n */\n #tryResolverFunction(clientId: string): string[] | null {\n try {\n const result = this.config.resolver!(clientId);\n if (Array.isArray(result)) {\n return result;\n }\n console.warn(\n `Permission resolver returned non-array for client ${clientId}, using fallback`\n );\n return null;\n } catch (error) {\n // Log message only, not full stack trace (this is expected fallback behavior)\n const message = error instanceof Error ? error.message : String(error);\n console.warn(\n `Permission resolver declined client ${clientId} (${message}), trying fallback`\n );\n return null;\n }\n }\n\n /**\n * Looks up permissions in the static map configuration.\n * Returns null if client is not found to allow fallback to defaults.\n * @param clientId - The unique identifier for the client\n * @returns Array of toolset names if found, null if client not in map\n * @private\n */\n #lookupStaticMap(clientId: string): string[] | null {\n const permissions = this.config.staticMap![clientId];\n if (permissions !== undefined) {\n return Array.isArray(permissions) ? permissions : [];\n }\n return null;\n }\n\n /**\n * Clears the permission cache.\n * Useful for cleanup during server shutdown or when permissions need to be refreshed.\n */\n clearCache(): void {\n this.cache.clear();\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport type { PermissionResolver } from \"./PermissionResolver.js\";\n\n/**\n * Context information extracted from a client request.\n * Used to identify the client and resolve their permissions.\n */\nexport interface ClientRequestContext {\n /**\n * Unique identifier for the client making the request.\n * May be provided via mcp-client-id header or generated as anonymous ID.\n */\n clientId: string;\n\n /**\n * Request headers that may contain permission data.\n * Used for header-based permission resolution.\n */\n headers?: Record<string, string>;\n}\n\n/**\n * Result of permission-aware bundle creation, including the resolved permissions.\n */\nexport interface PermissionAwareBundle {\n /**\n * The MCP server instance for this client.\n */\n server: McpServer;\n\n /**\n * The orchestrator managing toolsets for this client.\n */\n orchestrator: ServerOrchestrator;\n\n /**\n * The resolved permissions (allowed toolsets) for this client.\n * Contains only the toolsets that were successfully enabled.\n */\n allowedToolsets: string[];\n\n /**\n * Toolsets that failed to enable (e.g., invalid names).\n * Empty if all requested toolsets were enabled successfully.\n */\n failedToolsets: string[];\n}\n\n/**\n * Creates a permission-aware bundle creation function that wraps the original\n * createBundle function with permission resolution and enforcement.\n *\n * This function resolves client permissions and passes them to the bundle creator,\n * which creates a server with STATIC mode configured to only those toolsets.\n *\n * @param originalCreateBundle - Bundle creation function that accepts allowed toolsets\n * @param permissionResolver - Resolver instance for determining client permissions\n * @returns Enhanced bundle creation function that accepts client context\n */\nexport function createPermissionAwareBundle(\n originalCreateBundle: (allowedToolsets: string[]) => {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n },\n permissionResolver: PermissionResolver\n) {\n /**\n * Creates a server bundle with permission-based toolset access control.\n * Resolves client permissions and creates a server with those toolsets pre-loaded.\n *\n * This function is async to ensure toolsets are fully loaded before the server\n * is connected to a transport.\n *\n * @param context - Client request context containing ID and headers\n * @returns Promise resolving to server bundle with resolved permissions\n * @throws {Error} If all requested toolsets fail to enable\n */\n return async (\n context: ClientRequestContext\n ): Promise<PermissionAwareBundle> => {\n // Resolve permissions for this client\n const requestedToolsets = permissionResolver.resolvePermissions(\n context.clientId,\n context.headers\n );\n\n // Create bundle with allowed toolsets (STATIC mode pre-loads them)\n const bundle = originalCreateBundle(requestedToolsets);\n\n // Wait for toolsets to be enabled before returning\n // This ensures tools are registered before the server connects to transport\n const manager = bundle.orchestrator.getManager();\n\n const enabledToolsets: string[] = [];\n const failedToolsets: string[] = [];\n\n if (requestedToolsets.length > 0) {\n const result = await manager.enableToolsets(requestedToolsets);\n\n // Collect successful and failed toolsets\n for (const r of result.results) {\n if (r.success) {\n enabledToolsets.push(r.name);\n } else {\n failedToolsets.push(r.name);\n console.warn(\n `Failed to enable toolset '${r.name}' for client '${context.clientId}': ${r.message}`\n );\n }\n }\n\n // If ALL toolsets failed, this is likely a configuration error\n if (enabledToolsets.length === 0 && failedToolsets.length > 0) {\n throw new Error(\n `All requested toolsets failed to enable for client '${context.clientId}'. ` +\n `Requested: [${requestedToolsets.join(\", \")}]. ` +\n `Check that toolset names in permissions match the catalog.`\n );\n }\n }\n\n // Return bundle with resolved permissions\n return {\n server: bundle.server,\n orchestrator: bundle.orchestrator,\n allowedToolsets: enabledToolsets,\n failedToolsets,\n };\n };\n}\n","import Fastify, {\n type FastifyInstance,\n type FastifyReply,\n type FastifyRequest,\n} from \"fastify\";\nimport cors from \"@fastify/cors\";\nimport { randomUUID } from \"node:crypto\";\nimport type { DynamicToolManager } from \"../core/DynamicToolManager.js\";\nimport { ClientResourceCache } from \"../session/ClientResourceCache.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport type {\n ClientRequestContext,\n PermissionAwareBundle,\n} from \"./createPermissionAwareBundle.js\";\nimport type { CustomEndpointDefinition } from \"../http/customEndpoints.js\";\nimport { registerCustomEndpoints } from \"../http/endpointRegistration.js\";\n\nexport interface PermissionAwareFastifyTransportOptions {\n host?: string;\n port?: number;\n basePath?: string;\n cors?: boolean;\n logger?: boolean;\n app?: FastifyInstance;\n /**\n * Optional custom HTTP endpoints to register alongside MCP protocol endpoints.\n * Allows adding REST-like endpoints with Zod validation and type inference.\n * Handlers receive permission context (allowedToolsets, failedToolsets).\n */\n customEndpoints?: CustomEndpointDefinition[];\n}\n\n/**\n * Enhanced Fastify transport that supports permission-based toolset access.\n * Integrates with PermissionResolver to enforce per-client toolset permissions.\n * \n * This transport extracts client context from requests and passes it to the\n * permission-aware bundle creator, ensuring each client receives only their\n * authorized toolsets while maintaining session management and caching.\n */\nexport class PermissionAwareFastifyTransport {\n private readonly options: {\n host: string;\n port: number;\n basePath: string;\n cors: boolean;\n logger: boolean;\n app?: FastifyInstance;\n customEndpoints?: CustomEndpointDefinition[];\n };\n private readonly defaultManager: DynamicToolManager;\n private readonly createPermissionAwareBundle: (\n context: ClientRequestContext\n ) => Promise<PermissionAwareBundle>;\n private app: FastifyInstance | null = null;\n private readonly configSchema?: object;\n\n // Per-client server bundles and per-client session transports\n private readonly clientCache = new ClientResourceCache<{\n server: McpServer;\n orchestrator: ServerOrchestrator;\n sessions: Map<string, StreamableHTTPServerTransport>;\n allowedToolsets: string[];\n failedToolsets: string[];\n }>({\n onEvict: (_key, bundle) => {\n // Clean up all sessions when a client bundle is evicted\n this.#cleanupBundle(bundle);\n },\n });\n\n /**\n * Creates a new PermissionAwareFastifyTransport instance.\n * @param defaultManager - Default tool manager for status endpoints\n * @param createPermissionAwareBundle - Function to create permission-aware bundles\n * @param options - Transport configuration options\n * @param configSchema - Optional JSON schema for configuration discovery\n */\n constructor(\n defaultManager: DynamicToolManager,\n createPermissionAwareBundle: (\n context: ClientRequestContext\n ) => Promise<PermissionAwareBundle>,\n options: PermissionAwareFastifyTransportOptions = {},\n configSchema?: object\n ) {\n this.defaultManager = defaultManager;\n this.createPermissionAwareBundle = createPermissionAwareBundle;\n this.options = {\n host: options.host ?? \"0.0.0.0\",\n port: options.port ?? 3000,\n basePath: options.basePath ?? \"/\",\n cors: options.cors ?? true,\n logger: options.logger ?? false,\n app: options.app,\n customEndpoints: options.customEndpoints,\n };\n this.configSchema = configSchema;\n }\n\n /**\n * Starts the Fastify server and registers all MCP endpoints.\n * Sets up routes for health checks, tool status, and MCP protocol handling.\n */\n public async start(): Promise<void> {\n if (this.app) return;\n const app = this.options.app ?? Fastify({ logger: this.options.logger });\n if (this.options.cors) {\n await app.register(cors, { origin: true });\n }\n\n const base = this.#normalizeBasePath(this.options.basePath);\n\n this.#registerHealthEndpoint(app, base);\n this.#registerToolsEndpoint(app, base);\n this.#registerConfigDiscoveryEndpoint(app, base);\n this.#registerMcpPostEndpoint(app, base);\n this.#registerMcpGetEndpoint(app, base);\n this.#registerMcpDeleteEndpoint(app, base);\n\n // Register custom endpoints if provided with permission context\n // IMPORTANT: Only register if customEndpoints is provided AND has items\n if (this.options.customEndpoints && this.options.customEndpoints.length > 0) {\n registerCustomEndpoints(app, base, this.options.customEndpoints, {\n contextExtractor: async (req) => {\n // Extract client context from request\n const context = this.#extractClientContext(req);\n\n // Resolve permissions for this client\n try {\n const bundle = await this.createPermissionAwareBundle(context);\n return {\n allowedToolsets: bundle.allowedToolsets,\n failedToolsets: bundle.failedToolsets,\n };\n } catch (error) {\n // If permission resolution fails, return empty permissions\n console.warn(\n `Permission resolution failed for custom endpoint: ${error}`\n );\n return {\n allowedToolsets: [],\n failedToolsets: [],\n };\n }\n },\n });\n }\n\n // Only listen if we created the app\n if (!this.options.app) {\n await app.listen({ host: this.options.host, port: this.options.port });\n }\n this.app = app;\n }\n\n /**\n * Stops the Fastify server and cleans up all resources.\n * Closes all client sessions and clears the cache.\n */\n public async stop(): Promise<void> {\n if (!this.app) return;\n\n // Stop the cache pruning interval and clear all entries (triggers cleanup)\n this.clientCache.stop(true);\n\n if (!this.options.app) {\n await this.app.close();\n }\n this.app = null;\n }\n\n /**\n * Cleans up resources associated with a client bundle.\n * Closes all sessions within the bundle.\n * @param bundle - The client bundle to clean up\n * @private\n */\n #cleanupBundle(bundle: {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n sessions: Map<string, StreamableHTTPServerTransport>;\n allowedToolsets: string[];\n failedToolsets: string[];\n }): void {\n for (const [sessionId, transport] of bundle.sessions.entries()) {\n try {\n if (typeof (transport as any).close === \"function\") {\n (transport as any).close().catch((err: unknown) => {\n console.warn(`Error closing session ${sessionId}:`, err);\n });\n }\n } catch (err) {\n console.warn(`Error closing session ${sessionId}:`, err);\n }\n }\n bundle.sessions.clear();\n }\n\n /**\n * Normalizes the base path by removing trailing slashes.\n * @param basePath - The base path to normalize\n * @returns Normalized base path without trailing slash\n * @private\n */\n #normalizeBasePath(basePath: string): string {\n return basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n }\n\n /**\n * Registers the health check endpoint.\n * @param app - Fastify instance\n * @param base - Base path for routes\n * @private\n */\n #registerHealthEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/healthz`, async () => ({ ok: true }));\n }\n\n /**\n * Registers the tools status endpoint.\n * @param app - Fastify instance\n * @param base - Base path for routes\n * @private\n */\n #registerToolsEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/tools`, async () => this.defaultManager.getStatus());\n }\n\n /**\n * Registers the MCP configuration discovery endpoint.\n * @param app - Fastify instance\n * @param base - Base path for routes\n * @private\n */\n #registerConfigDiscoveryEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/.well-known/mcp-config`, async (_req, reply) => {\n reply.header(\"Content-Type\", \"application/schema+json; charset=utf-8\");\n const baseSchema = this.configSchema ?? {\n $schema: \"https://json-schema.org/draft/2020-12/schema\",\n title: \"MCP Session Configuration\",\n description: \"Schema for the /mcp endpoint configuration\",\n type: \"object\",\n properties: {},\n required: [],\n \"x-mcp-version\": \"1.0\",\n \"x-query-style\": \"dot+bracket\",\n };\n return baseSchema;\n });\n }\n\n /**\n * Registers the POST /mcp endpoint for JSON-RPC requests.\n * Extracts client context, resolves permissions, and handles MCP protocol.\n * @param app - Fastify instance\n * @param base - Base path for routes\n * @private\n */\n #registerMcpPostEndpoint(app: FastifyInstance, base: string): void {\n app.post(\n `${base}/mcp`,\n async (req: FastifyRequest, reply: FastifyReply) => {\n // Extract client context from request\n const context = this.#extractClientContext(req);\n\n // Determine if we should cache this client's bundle\n const useCache = !context.clientId.startsWith(\"anon-\");\n\n // Get or create permission-aware bundle for this client\n let bundle = useCache ? this.clientCache.get(context.clientId) : null;\n if (!bundle) {\n try {\n const created = await this.createPermissionAwareBundle(context);\n\n // Log any failed toolsets for debugging\n if (created.failedToolsets.length > 0) {\n console.warn(\n `Client ${context.clientId} had ${created.failedToolsets.length} toolsets fail to enable: ` +\n `[${created.failedToolsets.join(\", \")}]. ` +\n `Successfully enabled: [${created.allowedToolsets.join(\", \")}]`\n );\n }\n\n const providedSessions = (created as { sessions?: Map<string, StreamableHTTPServerTransport> }).sessions;\n bundle = {\n server: created.server,\n orchestrator: created.orchestrator,\n allowedToolsets: created.allowedToolsets,\n failedToolsets: created.failedToolsets,\n sessions:\n providedSessions instanceof Map ? providedSessions : new Map(),\n };\n if (useCache) this.clientCache.set(context.clientId, bundle);\n } catch (error) {\n // Handle permission resolution or bundle creation failures\n console.error(\n `Failed to create permission-aware bundle for client ${context.clientId}:`,\n error\n );\n reply.code(403);\n return this.#createSafeErrorResponse(\"Access denied\");\n }\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n\n let transport: StreamableHTTPServerTransport | undefined;\n if (sessionId && bundle.sessions.get(sessionId)) {\n transport = bundle.sessions.get(sessionId)!;\n } else if (!sessionId && isInitializeRequest((req as any).body)) {\n const newSessionId = randomUUID();\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => newSessionId,\n onsessioninitialized: (sid: string) => {\n bundle!.sessions.set(sid, transport!);\n },\n });\n try {\n await bundle.server.connect(transport);\n } catch (error) {\n reply.code(500);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Error initializing server.\" },\n id: null,\n };\n }\n transport.onclose = () => {\n if (transport?.sessionId)\n bundle!.sessions.delete(transport.sessionId);\n };\n } else {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Session not found or expired\" },\n id: null,\n };\n }\n\n // Delegate handling to SDK transport using raw Node req/res\n await transport.handleRequest(\n (req as any).raw,\n (reply as any).raw,\n (req as any).body\n );\n return reply;\n }\n );\n }\n\n /**\n * Registers the GET /mcp endpoint for SSE notifications.\n * @param app - Fastify instance\n * @param base - Base path for routes\n * @private\n */\n #registerMcpGetEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/mcp`, async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0 ? clientIdHeader : \"\";\n if (!clientId) {\n reply.code(400);\n return \"Missing mcp-client-id\";\n }\n const bundle = this.clientCache.get(clientId);\n if (!bundle) {\n reply.code(400);\n return \"Invalid or expired client\";\n }\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId) {\n reply.code(400);\n return \"Missing mcp-session-id\";\n }\n const transport = bundle.sessions.get(sessionId);\n if (!transport) {\n reply.code(400);\n return \"Invalid or expired session ID\";\n }\n await transport.handleRequest((req as any).raw, (reply as any).raw);\n return reply;\n });\n }\n\n /**\n * Registers the DELETE /mcp endpoint for session termination.\n * @param app - Fastify instance\n * @param base - Base path for routes\n * @private\n */\n #registerMcpDeleteEndpoint(app: FastifyInstance, base: string): void {\n app.delete(\n `${base}/mcp`,\n async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0 ? clientIdHeader : \"\";\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!clientId || !sessionId) {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32600,\n message: \"Missing mcp-client-id or mcp-session-id header\",\n },\n id: null,\n };\n }\n const bundle = this.clientCache.get(clientId);\n const transport = bundle?.sessions.get(sessionId);\n if (!bundle || !transport) {\n reply.code(404);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Session not found or expired\" },\n id: null,\n };\n }\n try {\n // Best-effort close and evict\n if (typeof (transport as any).close === \"function\") {\n try {\n await (transport as any).close();\n } catch {}\n }\n } finally {\n if (transport?.sessionId) bundle.sessions.delete(transport.sessionId);\n else bundle.sessions.delete(sessionId);\n }\n reply.code(204).send();\n return reply;\n }\n );\n }\n\n /**\n * Extracts client context from the request.\n * Generates anonymous client ID if not provided in headers.\n * @param req - Fastify request object\n * @returns Client request context with ID and headers\n * @private\n */\n #extractClientContext(req: FastifyRequest): ClientRequestContext {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0\n ? clientIdHeader\n : `anon-${randomUUID()}`;\n\n // Convert headers to plain object for permission resolution\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers[key] = value;\n }\n }\n\n return { clientId, headers };\n }\n\n /**\n * Creates a safe error response that doesn't expose unauthorized toolset information.\n * Used for permission-related errors to prevent information leakage.\n * @param message - Generic error message to return to client\n * @param code - JSON-RPC error code (default: -32000 for server error)\n * @returns JSON-RPC error response object\n * @private\n */\n #createSafeErrorResponse(message: string = \"Access denied\", code: number = -32000) {\n return {\n jsonrpc: \"2.0\" as const,\n error: {\n code,\n message,\n },\n id: null,\n };\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type {\n CreatePermissionBasedMcpServerOptions,\n ExposurePolicy,\n} from \"../types/index.js\";\nimport { validatePermissionConfig } from \"../permissions/validatePermissionConfig.js\";\nimport { validateSessionContextConfig } from \"../session/validateSessionContextConfig.js\";\nimport { PermissionResolver } from \"../permissions/PermissionResolver.js\";\nimport { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport { createPermissionAwareBundle } from \"../permissions/createPermissionAwareBundle.js\";\nimport { PermissionAwareFastifyTransport } from \"../permissions/PermissionAwareFastifyTransport.js\";\n\n/**\n * Validates and sanitizes exposure policy for permission-based servers.\n * Certain policy options are not applicable or could conflict with permission-based access control.\n * @param policy - The original exposure policy\n * @returns Sanitized policy safe for permission-based servers\n * @private\n */\nfunction sanitizeExposurePolicyForPermissions(\n policy?: ExposurePolicy\n): ExposurePolicy | undefined {\n if (!policy) return undefined;\n\n const sanitized: ExposurePolicy = {\n namespaceToolsWithSetKey: policy.namespaceToolsWithSetKey,\n };\n\n // Warn about ignored options\n if (policy.allowlist !== undefined) {\n console.warn(\n \"Permission-based servers: exposurePolicy.allowlist is ignored. \" +\n \"Allowed toolsets are determined by client permissions.\"\n );\n }\n if (policy.denylist !== undefined) {\n console.warn(\n \"Permission-based servers: exposurePolicy.denylist is ignored. \" +\n \"Use permission configuration to control toolset access.\"\n );\n }\n if (policy.maxActiveToolsets !== undefined) {\n console.warn(\n \"Permission-based servers: exposurePolicy.maxActiveToolsets is ignored. \" +\n \"Toolset count is determined by client permissions.\"\n );\n }\n if (policy.onLimitExceeded !== undefined) {\n console.warn(\n \"Permission-based servers: exposurePolicy.onLimitExceeded is ignored. \" +\n \"No toolset limits are enforced.\"\n );\n }\n\n return sanitized;\n}\n\n/**\n * Creates an MCP server with permission-based toolset access control.\n *\n * This function provides a separate API for creating servers where each client receives\n * only the toolsets they're authorized to access. Each client gets a fresh server instance\n * with STATIC mode configured to their allowed toolsets, ensuring per-client isolation\n * without meta-tools or dynamic loading.\n *\n * The server supports two permission sources:\n * - **Header-based**: Permissions are read from request headers (e.g., `mcp-toolset-permissions`)\n * - **Config-based**: Permissions are resolved server-side using static maps or resolver functions\n *\n * @param options - Configuration options including permission settings, catalog, and HTTP transport options\n * @returns Server instance with `server`, `start()`, and `close()` methods matching the createMcpServer interface\n * @throws {Error} If permission configuration is invalid, missing, or if startup.mode is provided\n *\n * @example\n * // Header-based permissions\n * const server = await createPermissionBasedMcpServer({\n * createServer: () => new McpServer({ name: \"my-server\", version: \"1.0.0\" }),\n * catalog: { toolsetA: { name: \"Toolset A\", tools: [...] } },\n * permissions: {\n * source: 'headers',\n * headerName: 'mcp-toolset-permissions' // optional, this is the default\n * }\n * });\n *\n * @example\n * // Config-based permissions with static map\n * const server = await createPermissionBasedMcpServer({\n * createServer: () => new McpServer({ name: \"my-server\", version: \"1.0.0\" }),\n * catalog: { toolsetA: { name: \"Toolset A\", tools: [...] } },\n * permissions: {\n * source: 'config',\n * staticMap: {\n * 'client-1': ['toolsetA', 'toolsetB'],\n * 'client-2': ['toolsetC']\n * },\n * defaultPermissions: [] // optional, defaults to empty array\n * }\n * });\n *\n * @example\n * // Config-based permissions with resolver function\n * const server = await createPermissionBasedMcpServer({\n * createServer: () => new McpServer({ name: \"my-server\", version: \"1.0.0\" }),\n * catalog: { toolsetA: { name: \"Toolset A\", tools: [...] } },\n * permissions: {\n * source: 'config',\n * resolver: (clientId) => {\n * // Your custom logic to determine permissions\n * return clientId.startsWith('admin-') ? ['toolsetA', 'toolsetB'] : ['toolsetA'];\n * },\n * defaultPermissions: ['toolsetA'] // fallback if resolver fails\n * }\n * });\n */\nexport async function createPermissionBasedMcpServer(\n options: CreatePermissionBasedMcpServerOptions\n) {\n // Validate that permissions field is provided\n if (!options.permissions) {\n throw new Error(\n \"Permission configuration is required for createPermissionBasedMcpServer. \" +\n \"Please provide a 'permissions' field in the options.\"\n );\n }\n\n // Validate permission configuration\n validatePermissionConfig(options.permissions);\n\n // Validate session context configuration if provided\n if (options.sessionContext) {\n validateSessionContextConfig(options.sessionContext);\n // Note: Session context is validated but not yet fully implemented\n // for permission-based servers. The base context is used for module loaders.\n console.warn(\n \"Session context support for permission-based servers is limited. \" +\n \"The base context will be used for module loaders.\"\n );\n }\n\n // Prevent startup.mode configuration - permissions determine toolsets\n if ((options as any).startup) {\n throw new Error(\n \"Permission-based servers determine toolsets from client permissions. \" +\n \"The 'startup' option is not allowed. Remove it from your configuration.\"\n );\n }\n\n // Validate createServer factory is provided\n if (typeof options.createServer !== \"function\") {\n throw new Error(\n \"createPermissionBasedMcpServer: `createServer` (factory) is required\"\n );\n }\n\n // Sanitize exposure policy for permission-based operation\n const sanitizedPolicy = sanitizeExposurePolicyForPermissions(\n options.exposurePolicy\n );\n\n // Create permission resolver instance\n const permissionResolver = new PermissionResolver(options.permissions);\n\n // Create base server for default manager (used for status endpoints)\n const baseServer: McpServer = options.createServer();\n\n // Create base orchestrator for default manager (empty toolsets for status endpoint)\n // No notifier needed - STATIC mode with fixed toolsets per client\n const baseOrchestrator = new ServerOrchestrator({\n server: baseServer,\n catalog: options.catalog,\n moduleLoaders: options.moduleLoaders,\n exposurePolicy: sanitizedPolicy,\n context: options.context,\n notifyToolsListChanged: undefined, // No notifications in STATIC mode\n startup: { mode: \"STATIC\", toolsets: [] },\n registerMetaTools: false,\n });\n\n // Create permission-aware bundle creator\n const createBundle = createPermissionAwareBundle(\n (allowedToolsets: string[]) => {\n // Create fresh server and orchestrator for each client\n // Use STATIC mode but don't auto-enable toolsets in constructor\n // We'll enable them manually in createPermissionAwareBundle to ensure they're loaded before connection\n const clientServer: McpServer = options.createServer();\n const clientOrchestrator = new ServerOrchestrator({\n server: clientServer,\n catalog: options.catalog,\n moduleLoaders: options.moduleLoaders,\n exposurePolicy: sanitizedPolicy,\n context: options.context,\n notifyToolsListChanged: undefined, // No notifications in STATIC mode\n startup: { mode: \"STATIC\", toolsets: [] }, // Empty - we'll enable manually\n registerMetaTools: false, // No meta-tools - toolsets are fixed per client\n });\n return { server: clientServer, orchestrator: clientOrchestrator };\n },\n permissionResolver\n );\n\n // Create permission-aware transport\n const transport = new PermissionAwareFastifyTransport(\n baseOrchestrator.getManager(),\n createBundle,\n options.http,\n options.configSchema\n );\n\n // Return same interface as createMcpServer\n return {\n server: baseServer,\n start: async () => {\n await transport.start();\n },\n close: async () => {\n try {\n // Stop the transport (cleans up client contexts)\n await transport.stop();\n } finally {\n // Clear permission cache\n permissionResolver.clearCache();\n }\n },\n };\n}\n","import { z } from \"zod\";\n\n/**\n * Supported HTTP methods for custom endpoints\n */\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\n\n/**\n * Request context passed to custom endpoint handlers.\n * Contains validated and typed request data without exposing Fastify types.\n */\nexport interface CustomEndpointRequest<\n TBody = unknown,\n TQuery = unknown,\n TParams = unknown\n> {\n /**\n * Validated request body (typed from bodySchema)\n */\n body: TBody;\n\n /**\n * Validated query parameters (typed from querySchema)\n */\n query: TQuery;\n\n /**\n * Validated path parameters (typed from paramsSchema)\n */\n params: TParams;\n\n /**\n * Raw request headers\n */\n headers: Record<string, string | string[] | undefined>;\n\n /**\n * Client ID (from mcp-client-id header or auto-generated for anonymous clients)\n */\n clientId: string;\n}\n\n/**\n * Permission-aware request context for custom endpoints in permission-based servers.\n * Extends CustomEndpointRequest with permission information.\n */\nexport interface PermissionAwareEndpointRequest<\n TBody = unknown,\n TQuery = unknown,\n TParams = unknown\n> extends CustomEndpointRequest<TBody, TQuery, TParams> {\n /**\n * Toolsets this client is allowed to access (resolved from permissions)\n */\n allowedToolsets: string[];\n\n /**\n * Toolsets that failed to enable for this client\n */\n failedToolsets: string[];\n}\n\n/**\n * Handler function type with automatic type inference from Zod schemas.\n * Receives validated and typed request data, returns typed response.\n */\nexport type CustomEndpointHandler<\n TBody extends z.ZodTypeAny,\n TQuery extends z.ZodTypeAny,\n TParams extends z.ZodTypeAny,\n TResponse extends z.ZodTypeAny\n> = (\n request: CustomEndpointRequest<\n TBody extends z.ZodTypeAny ? z.infer<TBody> : never,\n TQuery extends z.ZodTypeAny ? z.infer<TQuery> : never,\n TParams extends z.ZodTypeAny ? z.infer<TParams> : never\n >\n) => Promise<z.infer<TResponse>> | z.infer<TResponse>;\n\n/**\n * Permission-aware handler function type for permission-based servers.\n * Receives permission context in addition to validated request data.\n */\nexport type PermissionAwareEndpointHandler<\n TBody extends z.ZodTypeAny,\n TQuery extends z.ZodTypeAny,\n TParams extends z.ZodTypeAny,\n TResponse extends z.ZodTypeAny\n> = (\n request: PermissionAwareEndpointRequest<\n TBody extends z.ZodTypeAny ? z.infer<TBody> : never,\n TQuery extends z.ZodTypeAny ? z.infer<TQuery> : never,\n TParams extends z.ZodTypeAny ? z.infer<TParams> : never\n >\n) => Promise<z.infer<TResponse>> | z.infer<TResponse>;\n\n/**\n * Custom HTTP endpoint definition with Zod schema-based validation and type inference.\n * Allows defining REST-like endpoints alongside MCP protocol endpoints.\n *\n * @template TBody - Zod schema for request body validation\n * @template TQuery - Zod schema for query parameter validation\n * @template TParams - Zod schema for path parameter validation\n * @template TResponse - Zod schema for response validation\n *\n * @example\n * ```typescript\n * const getUserEndpoint = defineEndpoint({\n * method: \"GET\",\n * path: \"/users/:userId\",\n * paramsSchema: z.object({\n * userId: z.string().uuid(),\n * }),\n * responseSchema: z.object({\n * id: z.string(),\n * name: z.string(),\n * }),\n * handler: async (req) => {\n * // req.params is typed: { userId: string }\n * const { userId } = req.params;\n * return { id: userId, name: \"Alice\" };\n * },\n * });\n * ```\n */\nexport interface CustomEndpointDefinition<\n TBody extends z.ZodTypeAny = z.ZodTypeAny,\n TQuery extends z.ZodTypeAny = z.ZodTypeAny,\n TParams extends z.ZodTypeAny = z.ZodTypeAny,\n TResponse extends z.ZodTypeAny = z.ZodTypeAny\n> {\n /**\n * HTTP method for this endpoint\n */\n method: HttpMethod;\n\n /**\n * URL path (relative to basePath). Supports path parameters using :param syntax.\n *\n * @example\n * - \"/users\" - Simple path\n * - \"/users/:id\" - Path with single parameter\n * - \"/items/:category/:id\" - Path with multiple parameters\n */\n path: string;\n\n /**\n * Optional Zod schema for request body validation (typically used with POST, PUT, PATCH).\n * Enables automatic type inference for handler body parameter.\n */\n bodySchema?: TBody;\n\n /**\n * Optional Zod schema for query parameter validation.\n * Enables automatic type inference for handler query parameter.\n *\n * @example\n * ```typescript\n * querySchema: z.object({\n * limit: z.coerce.number().int().positive().default(10),\n * offset: z.coerce.number().int().nonnegative().default(0),\n * })\n * ```\n */\n querySchema?: TQuery;\n\n /**\n * Optional Zod schema for path parameter validation.\n * Enables automatic type inference for handler params parameter.\n */\n paramsSchema?: TParams;\n\n /**\n * Optional Zod schema for response validation.\n * Enables automatic type inference for handler return type.\n * If validation fails, returns 500 error to prevent information leakage.\n */\n responseSchema?: TResponse;\n\n /**\n * Request handler function with inferred types from schemas.\n * Receives validated and typed request data, returns typed response.\n */\n handler: CustomEndpointHandler<TBody, TQuery, TParams, TResponse>;\n\n /**\n * Optional description for documentation purposes\n */\n description?: string;\n}\n\n/**\n * Standard error response structure for custom endpoints\n */\nexport interface EndpointErrorResponse {\n error: {\n /**\n * Error code indicating the type of error\n * - VALIDATION_ERROR: Request validation failed (400)\n * - INTERNAL_ERROR: Handler threw an error (500)\n * - RESPONSE_VALIDATION_ERROR: Response validation failed (500)\n */\n code: \"VALIDATION_ERROR\" | \"INTERNAL_ERROR\" | \"RESPONSE_VALIDATION_ERROR\";\n\n /**\n * Human-readable error message\n */\n message: string;\n\n /**\n * Optional additional error details (e.g., Zod validation errors)\n */\n details?: unknown;\n };\n}\n\n/**\n * Helper function to create type-safe custom endpoints with automatic type inference.\n * Provides better IntelliSense and type checking for endpoint definitions.\n *\n * @template TBody - Zod schema for request body\n * @template TQuery - Zod schema for query parameters\n * @template TParams - Zod schema for path parameters\n * @template TResponse - Zod schema for response\n *\n * @param definition - Endpoint definition with schemas and handler\n * @returns The same endpoint definition with full type inference\n *\n * @example\n * ```typescript\n * import { z } from \"zod\";\n * import { defineEndpoint } from \"toolception\";\n *\n * const getUsersEndpoint = defineEndpoint({\n * method: \"GET\",\n * path: \"/users\",\n * querySchema: z.object({\n * limit: z.coerce.number().int().positive().default(10),\n * role: z.enum([\"admin\", \"user\"]).optional(),\n * }),\n * responseSchema: z.object({\n * users: z.array(z.object({\n * id: z.string(),\n * name: z.string(),\n * })),\n * total: z.number(),\n * }),\n * handler: async (req) => {\n * // req.query is fully typed: { limit: number, role?: \"admin\" | \"user\" }\n * const { limit, role } = req.query;\n *\n * return {\n * users: [{ id: \"1\", name: \"Alice\" }],\n * total: 1,\n * };\n * },\n * });\n * ```\n */\nexport function defineEndpoint<\n TBody extends z.ZodTypeAny = z.ZodNever,\n TQuery extends z.ZodTypeAny = z.ZodNever,\n TParams extends z.ZodTypeAny = z.ZodNever,\n TResponse extends z.ZodTypeAny = z.ZodAny\n>(\n definition: CustomEndpointDefinition<TBody, TQuery, TParams, TResponse>\n): CustomEndpointDefinition<TBody, TQuery, TParams, TResponse> {\n return definition;\n}\n\n/**\n * Helper function to create permission-aware custom endpoints for permission-based servers.\n * Similar to defineEndpoint but with access to permission context in the handler.\n *\n * @template TBody - Zod schema for request body\n * @template TQuery - Zod schema for query parameters\n * @template TParams - Zod schema for path parameters\n * @template TResponse - Zod schema for response\n *\n * @param definition - Endpoint definition with permission-aware handler\n * @returns Endpoint definition compatible with permission-based servers\n *\n * @example\n * ```typescript\n * import { definePermissionAwareEndpoint } from \"toolception\";\n *\n * const statsEndpoint = definePermissionAwareEndpoint({\n * method: \"GET\",\n * path: \"/my-permissions\",\n * responseSchema: z.object({\n * toolsets: z.array(z.string()),\n * count: z.number(),\n * }),\n * handler: async (req) => {\n * // req.allowedToolsets and req.failedToolsets are available\n * return {\n * toolsets: req.allowedToolsets,\n * count: req.allowedToolsets.length,\n * };\n * },\n * });\n * ```\n */\nexport function definePermissionAwareEndpoint<\n TBody extends z.ZodTypeAny = z.ZodNever,\n TQuery extends z.ZodTypeAny = z.ZodNever,\n TParams extends z.ZodTypeAny = z.ZodNever,\n TResponse extends z.ZodTypeAny = z.ZodAny\n>(definition: {\n method: HttpMethod;\n path: string;\n bodySchema?: TBody;\n querySchema?: TQuery;\n paramsSchema?: TParams;\n responseSchema?: TResponse;\n handler: PermissionAwareEndpointHandler<TBody, TQuery, TParams, TResponse>;\n description?: string;\n}): CustomEndpointDefinition<TBody, TQuery, TParams, TResponse> {\n // Internal conversion: permission-aware handler is compatible with standard handler\n // The permission fields will be injected by the registration logic\n return definition as any;\n}\n"],"names":["DEFAULT_KEYS","ToolsetValidator","options","__publicField","env","args","input","catalog","raw","s","valid","result","name","toolsets","modules","def","m","sanitized","toolsetNames","error","source","key","value","RESERVED_TOOLSET_KEYS","ModuleResolver","context","collected","modKey","loader","loaded","err","ToolingError","message","code","details","_options","ToolRegistry","toolsetKey","toolName","set","tools","t","safe","k","v","DynamicToolManager","toolsetName","skipNotification","validation","policyCheck","registeredTools","resolvedTools","mapped","tool","activeToolsets","results","res","successAll","r","anySuccess","all","META_TOOLSET_KEY","registerMetaTools","server","manager","toolRegistry","z","available","byToolset","items","payload","status","ServerOrchestrator","startup","resolved","initial","names","isValid","ClientResourceCache","__privateAdd","_ClientResourceCache_instances","pruneEvery","entry","resource","newEntry","__privateMethod","callEvictCallback_fn","clearEntries","entries","lruKey","now","keysToDelete","registerCustomEndpoints","app","basePath","endpoints","reservedPaths","endpoint","fullPath","reserved","method","req","reply","clientIdHeader","clientId","randomUUID","body","bodyResult","createValidationError","query","queryResult","params","paramsResult","customRequest","additionalContext","responseResult","field","FastifyTransport","defaultManager","createBundle","configSchema","sessionContextResolver","baseContext","_key","bundle","Fastify","cors","base","_req","useCache","cacheKey","mergedContext","created","sessionId","transport","isInitializeRequest","newSessionId","StreamableHTTPServerTransport","sid","sessionRequestContext","headers","rawQuery","SessionContextResolver","config","request","parsedConfig","rawValue","jsonString","parsed","filtered","sessionConfig","override","baseValue","sortedKeys","normalizedObj","createHash","validateSessionContextConfig","validateConfigExists","validateEnabledField","validateQueryParamConfig","validateContextResolver","validateMergeStrategy","i","startupConfigSchema","createMcpServer","formatted","mode","baseServer","hasNotifierA","hasNotifierB","notifyToolsChanged","target","orchestrator","effectiveContext","createdServer","createdOrchestrator","validatePermissionConfig","validateSourceField","validateConfigBasedPermissions","validateTypes","validateStaticMapValues","staticMap","permissions","PermissionResolver","_PermissionResolver_instances","parseHeaderPermissions_fn","resolveConfigPermissions_fn","headerValue","findHeaderCaseInsensitive_fn","normalizedKey","resolverResult","tryResolverFunction_fn","staticResult","lookupStaticMap_fn","createPermissionAwareBundle","originalCreateBundle","permissionResolver","requestedToolsets","enabledToolsets","failedToolsets","PermissionAwareFastifyTransport","_PermissionAwareFastifyTransport_instances","cleanupBundle_fn","normalizeBasePath_fn","registerHealthEndpoint_fn","registerToolsEndpoint_fn","registerConfigDiscoveryEndpoint_fn","registerMcpPostEndpoint_fn","registerMcpGetEndpoint_fn","registerMcpDeleteEndpoint_fn","extractClientContext_fn","providedSessions","createSafeErrorResponse_fn","sanitizeExposurePolicyForPermissions","policy","createPermissionBasedMcpServer","sanitizedPolicy","baseOrchestrator","allowedToolsets","clientServer","clientOrchestrator","defineEndpoint","definition","definePermissionAwareEndpoint"],"mappings":";;;;;;;;;;;;;;AAWA,MAAMA,IAA2C;AAAA,EAC/C,SAAS;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAAA,EAEF,UAAU,CAAC,aAAa,YAAY,eAAe;AACrD;AAEO,MAAMC,GAAiB;AAAA,EAG5B,YAAYC,IAA+B,IAAI;AAF9B,IAAAC,EAAA;AAGf,SAAK,OAAO;AAAA,MACV,SAASD,EAAQ,MAAM,WAAWF,EAAa;AAAA,MAC/C,UAAUE,EAAQ,MAAM,YAAYF,EAAa;AAAA,IAAA;AAAA,EAErD;AAAA,EAEO,YACLI,GACAC,GACa;AAEb,WAAI,KAAK,iBAAiBA,CAAI,IAAU,YAEf,KAAK,kBAAkBA,CAAI,IACvB,WAGzB,KAAK,iBAAiBD,CAAG,IAAU,YAEf,KAAK,kBAAkBA,CAAG,IACtB,WAErB;AAAA,EACT;AAAA,EAEO,4BACLE,GACAC,GACU;AACV,QAAI,CAACD,KAAS,OAAOA,KAAU,iBAAiB,CAAA;AAChD,UAAME,IAAMF,EACT,MAAM,GAAG,EACT,IAAI,CAACG,MAAMA,EAAE,KAAA,CAAM,EACnB,OAAO,CAACA,MAAMA,EAAE,SAAS,CAAC,GAEvBC,IAAQ,IAAI,IAAI,OAAO,KAAKH,CAAO,CAAC,GACpCI,IAAmB,CAAA;AACzB,eAAWC,KAAQJ;AACjB,MAAIE,EAAM,IAAIE,CAAI,IAAGD,EAAO,KAAKC,CAAI,IAEnC,QAAQ;AAAA,QACN,oBAAoBA,CAAI,yBAAyB,MAAM;AAAA,UACrDF;AAAA,QAAA,EACA,KAAK,IAAI,CAAC;AAAA,MAAA;AAGlB,WAAOC;AAAA,EACT;AAAA,EAEO,sBACLE,GACAN,GACU;AACV,UAAMO,wBAAc,IAAA;AACpB,eAAWF,KAAQC,GAAU;AAC3B,YAAME,IAAMR,EAAQK,CAAI;AACxB,MAAKG,MACJA,EAAI,WAAW,CAAA,GAAI,QAAQ,CAACC,MAAMF,EAAQ,IAAIE,CAAC,CAAC;AAAA,IACnD;AACA,WAAO,MAAM,KAAKF,CAAO;AAAA,EAC3B;AAAA,EAEO,oBACLF,GACAL,GAC0D;AAC1D,QAAI,CAACK,KAAQ,OAAOA,KAAS;AAC3B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,kFAAkF,OAAO;AAAA,UAC9FL;AAAA,QAAA,EACA,KAAK,IAAI,CAAC;AAAA,MAAA;AAGhB,UAAMU,IAAYL,EAAK,KAAA;AACvB,WAAIK,EAAU,WAAW,IAChB;AAAA,MACL,SAAS;AAAA,MACT,OAAO,oDAAoD,OAAO;AAAA,QAChEV;AAAA,MAAA,EACA,KAAK,IAAI,CAAC;AAAA,IAAA,IAGXA,EAAQU,CAAS,IAQf,EAAE,SAAS,IAAM,WAAAA,EAAA,IAPf;AAAA,MACL,SAAS;AAAA,MACT,OAAO,YAAYA,CAAS,oCAAoC,OAAO;AAAA,QACrEV;AAAA,MAAA,EACA,KAAK,IAAI,CAAC;AAAA,IAAA;AAAA,EAIlB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASO,uBACLW,GACAX,GAC0D;AAC1D,QAAI;AAEF,iBAAWK,KAAQM;AACjB,YAAI,CAACX,EAAQK,CAAI;AACf,iBAAO;AAAA,YACL,SAAS;AAAA,YACT,OAAO,YAAYA,CAAI;AAAA,UAAA;AAO7B,aAAO,EAAE,SAAS,IAAM,SADR,KAAK,sBAAsBM,GAAcX,CAAO,EACxC;AAAA,IAC1B,SAASY,GAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,+BAA+BD,EAAa,KAAK,IAAI,CAAC,KAC3DC,aAAiB,QAAQA,EAAM,UAAU,eAC3C;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA,EAEQ,iBACNC,GACS;AACT,QAAI,CAACA,EAAQ,QAAO;AACpB,eAAWC,KAAO,KAAK,KAAK,SAAS;AACnC,YAAMC,IAASF,EAAeC,CAAG;AAEjC,UADIC,MAAU,MACV,OAAOA,KAAU,YACTA,EAAM,KAAA,EAAO,YAAA,MACb;AAAQ,eAAO;AAAA,IAE7B;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,kBACNF,GACoB;AACpB,QAAKA;AACL,iBAAWC,KAAO,KAAK,KAAK,UAAU;AACpC,cAAMC,IAASF,EAAeC,CAAG;AACjC,YAAI,OAAOC,KAAU,YAAYA,EAAM,KAAA,EAAO,SAAS;AACrD,iBAAOA;AAAA,MACX;AAAA,EAEF;AACF;ACxKA,MAAMC,IAAwB,CAAC,OAAO;AAO/B,MAAMC,GAAe;AAAA,EAI1B,YAAYtB,GAAgC;AAH3B,IAAAC,EAAA;AACA,IAAAA,EAAA;AAIf,eAAWkB,KAAOE;AAChB,UAAIF,KAAOnB,EAAQ;AACjB,cAAM,IAAI;AAAA,UACR,gBAAgBmB,CAAG;AAAA,QAAA;AAIzB,SAAK,UAAUnB,EAAQ,SACvB,KAAK,gBAAgBA,EAAQ,iBAAiB,CAAA;AAAA,EAChD;AAAA,EAEO,uBAAiC;AACtC,WAAO,OAAO,KAAK,KAAK,OAAO;AAAA,EACjC;AAAA,EAEO,qBAAqBU,GAA6C;AACvE,WAAO,KAAK,QAAQA,CAAI;AAAA,EAC1B;AAAA,EAEO,oBAAoBA,GAIzB;AACA,QAAI,CAACA,KAAQ,OAAOA,KAAS;AAC3B,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,kFAAkF,KAAK,qBAAA,EAAuB;AAAA,UACnH;AAAA,QAAA,CACD;AAAA,MAAA;AAGL,UAAMK,IAAYL,EAAK,KAAA;AACvB,WAAIK,EAAU,WAAW,IAChB;AAAA,MACL,SAAS;AAAA,MACT,OAAO,oDAAoD,KAAK,qBAAA,EAAuB;AAAA,QACrF;AAAA,MAAA,CACD;AAAA,IAAA,IAIDM,EAAsB,SAASN,CAAS,IACnC;AAAA,MACL,SAAS;AAAA,MACT,OAAO,gBAAgBA,CAAS;AAAA,IAAA,IAG/B,KAAK,QAAQA,CAAS,IAQpB,EAAE,SAAS,IAAM,WAAAA,EAAA,IAPf;AAAA,MACL,SAAS;AAAA,MACT,OAAO,YAAYA,CAAS,oCAAoC,KAAK,uBAAuB;AAAA,QAC1F;AAAA,MAAA,CACD;AAAA,IAAA;AAAA,EAIP;AAAA,EAEA,MAAa,wBACXJ,GACAY,GAC8B;AAC9B,UAAMC,IAAiC,CAAA;AACvC,eAAWd,KAAQC,GAAU;AAC3B,YAAME,IAAM,KAAK,QAAQH,CAAI;AAC7B,UAAKG,MACD,MAAM,QAAQA,EAAI,KAAK,KAAKA,EAAI,MAAM,SAAS,KACjDW,EAAU,KAAK,GAAGX,EAAI,KAAK,GAEzB,MAAM,QAAQA,EAAI,OAAO,KAAKA,EAAI,QAAQ,SAAS;AACrD,mBAAWY,KAAUZ,EAAI,SAAS;AAChC,gBAAMa,IAAS,KAAK,cAAcD,CAAM;AACxC,cAAKC;AACL,gBAAI;AACF,oBAAMC,IAAS,MAAMD,EAAOH,CAAO;AACnC,cAAI,MAAM,QAAQI,CAAM,KAAKA,EAAO,SAAS,KAC3CH,EAAU,KAAK,GAAGG,CAAM;AAAA,YAE5B,SAASC,GAAK;AACZ,sBAAQ;AAAA,gBACN,kBAAkBH,CAAM,yBAAyBf,CAAI;AAAA,gBACrDkB;AAAA,cAAA;AAAA,YAEJ;AAAA,QACF;AAAA,IAEJ;AACA,WAAOJ;AAAA,EACT;AACF;AChHO,MAAMK,UAAqB,MAAM;AAAA,EAItC,YACEC,GACAC,GACAC,GACAC,GACA;AACA,UAAMH,CAAO;AATC,IAAA7B,EAAA;AACA,IAAAA,EAAA;AASd,SAAK,OAAO,gBACZ,KAAK,OAAO8B,GACZ,KAAK,UAAUC;AAAA,EACjB;AACF;ACVO,MAAME,EAAa;AAAA,EAKxB,YAAYlC,IAA+B,IAAI;AAJ9B,IAAAC,EAAA;AACA,IAAAA,EAAA,mCAAY,IAAA;AACZ,IAAAA,EAAA,4CAAqB,IAAA;AAGpC,SAAK,UAAU;AAAA,MACb,sBAAsBD,EAAQ,wBAAwB;AAAA,IAAA;AAAA,EAE1D;AAAA,EAEO,YAAYmC,GAAoBC,GAA0B;AAE/D,WADI,CAAC,KAAK,QAAQ,wBACdA,EAAS,WAAW,GAAGD,CAAU,GAAG,IAAUC,IAC3C,GAAGD,CAAU,IAAIC,CAAQ;AAAA,EAClC;AAAA,EAEO,IAAI1B,GAAuB;AAChC,WAAO,KAAK,MAAM,IAAIA,CAAI;AAAA,EAC5B;AAAA,EAEO,IAAIA,GAAoB;AAC7B,QAAI,KAAK,MAAM,IAAIA,CAAI;AACrB,YAAM,IAAImB;AAAA,QACR,yBAAyBnB,CAAI;AAAA,QAC7B;AAAA,MAAA;AAGJ,SAAK,MAAM,IAAIA,CAAI;AAAA,EACrB;AAAA,EAEO,cAAcyB,GAAoBzB,GAAoB;AAC3D,SAAK,IAAIA,CAAI;AACb,UAAM2B,IAAM,KAAK,eAAe,IAAIF,CAAU,yBAAS,IAAA;AACvD,IAAAE,EAAI,IAAI3B,CAAI,GACZ,KAAK,eAAe,IAAIyB,GAAYE,CAAG;AAAA,EACzC;AAAA,EAEO,eACLF,GACAG,GACqB;AACrB,WAAOA,EAAM,IAAI,CAACC,MAAM;AACtB,YAAMC,IAAO,KAAK,YAAYL,GAAYI,EAAE,IAAI;AAChD,UAAI,KAAK,IAAIC,CAAI;AACf,cAAM,IAAIX;AAAA,UACR,4BAA4BW,CAAI;AAAA,UAChC;AAAA,QAAA;AAGJ,aAAO,EAAE,GAAGD,GAAG,MAAMC,EAAA;AAAA,IACvB,CAAC;AAAA,EACH;AAAA,EAEO,OAAiB;AACtB,WAAO,MAAM,KAAK,KAAK,KAAK;AAAA,EAC9B;AAAA,EAEO,gBAA0C;AAC/C,UAAM/B,IAAmC,CAAA;AACzC,eAAW,CAACgC,GAAGC,CAAC,KAAK,KAAK,eAAe;AACvC,MAAAjC,EAAOgC,CAAC,IAAI,MAAM,KAAKC,CAAC;AAE1B,WAAOjC;AAAA,EACT;AACF;ACrDO,MAAMkC,GAAmB;AAAA,EAU9B,YAAY3C,GAAoC;AAT/B,IAAAC,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AAEA,IAAAA,EAAA,4CAAqB,IAAA;AAGpC,SAAK,SAASD,EAAQ,QACtB,KAAK,WAAWA,EAAQ,UACxB,KAAK,UAAUA,EAAQ,SACvB,KAAK,qBAAqBA,EAAQ,oBAClC,KAAK,iBAAiBA,EAAQ,gBAC9B,KAAK,eACHA,EAAQ,gBAAgB,IAAIkC,EAAa,EAAE,sBAAsB,IAAM;AAAA,EAC3E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,qBAAoC;AAChD,QAAK,KAAK;AACV,UAAI;AACF,cAAM,KAAK,mBAAA;AAAA,MACb,SAASN,GAAK;AACZ,gBAAQ,KAAK,iDAAiDA,CAAG;AAAA,MACnE;AAAA,EACF;AAAA,EAEO,uBAAiC;AACtC,WAAO,KAAK,SAAS,qBAAA;AAAA,EACvB;AAAA,EAEO,oBAA8B;AACnC,WAAO,MAAM,KAAK,KAAK,cAAc;AAAA,EACvC;AAAA,EAEO,qBAAqBlB,GAA6C;AACvE,WAAO,KAAK,SAAS,qBAAqBA,CAAI;AAAA,EAChD;AAAA,EAEO,SAASA,GAAuB;AACrC,WAAO,KAAK,eAAe,IAAIA,CAAI;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAa,cACXkC,GACAC,IAAmB,IAC6B;AAChD,UAAMC,IAAa,KAAK,SAAS,oBAAoBF,CAAW;AAChE,QAAI,CAACE,EAAW,WAAW,CAACA,EAAW;AACrC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAASA,EAAW,SAAS;AAAA,MAAA;AAGjC,UAAM/B,IAAY+B,EAAW;AAC7B,QAAI,KAAK,eAAe,IAAI/B,CAAS;AACnC,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,YAAYA,CAAS;AAAA,MAAA;AAKlC,UAAMgC,IAAc,KAAK,oBAAoBhC,CAAS;AACtD,QAAI,CAACgC,EAAY;AACf,aAAO,EAAE,SAAS,IAAO,SAASA,EAAY,QAAA;AAIhD,UAAMC,IAA4B,CAAA;AAElC,QAAI;AACF,YAAMC,IAAgB,MAAM,KAAK,SAAS;AAAA,QACxC,CAAClC,CAAS;AAAA,QACV,KAAK;AAAA,MAAA;AAIP,UAAIkC,KAAiBA,EAAc,SAAS,GAAG;AAC7C,cAAMC,IAAS,KAAK,aAAa;AAAA,UAC/BnC;AAAA,UACAkC;AAAA,QAAA;AAEF,mBAAWE,KAAQD;AACjB,eAAK,mBAAmBC,GAAMpC,CAAS,GACvCiC,EAAgB,KAAKG,EAAK,IAAI;AAAA,MAElC;AAGA,kBAAK,eAAe,IAAIpC,CAAS,GAG5B8B,KACH,MAAM,KAAK,mBAAA,GAGN;AAAA,QACL,SAAS;AAAA,QACT,SAAS,YAAY9B,CAAS,sCAC5BkC,GAAe,UAAU,CAC3B;AAAA,MAAA;AAAA,IAEJ,SAAShC,GAAO;AAEd,aAAI+B,EAAgB,SAAS,KAC3B,QAAQ;AAAA,QACN,qCAAqCjC,CAAS,MACzCiC,EAAgB,MAAM,yGAC0BA,EAAgB,KAAK,IAAI,CAAC;AAAA,MAAA,GAI5E;AAAA,QACL,SAAS;AAAA,QACT,SAAS,6BAA6BjC,CAAS,MAC7CE,aAAiB,QAAQA,EAAM,UAAU,eAC3C;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,oBAAoB2B,GAG1B;AACA,WACE,KAAK,gBAAgB,aACrB,CAAC,KAAK,eAAe,UAAU,SAASA,CAAW,IAE5C;AAAA,MACL,SAAS;AAAA,MACT,SAAS,YAAYA,CAAW;AAAA,IAAA,IAIlC,KAAK,gBAAgB,YACrB,KAAK,eAAe,SAAS,SAASA,CAAW,IAE1C;AAAA,MACL,SAAS;AAAA,MACT,SAAS,YAAYA,CAAW;AAAA,IAAA,IAGhC,KAAK,gBAAgB,sBAAsB,UAChC,KAAK,eAAe,OAAO,IAC7B,KAAK,eAAe,qBAC7B,KAAK,eAAe;AAAA,MAClB,CAACA,CAAW;AAAA,MACZ,MAAM,KAAK,KAAK,cAAc;AAAA,IAAA,GAEzB;AAAA,MACL,SAAS;AAAA,MACT,SAAS,yCAAyC,KAAK,eAAe,iBAAiB;AAAA,IAAA,KAItF,EAAE,SAAS,IAAM,SAAS,GAAA;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,mBAAmBO,GAAyBhB,GAA0B;AAK5E,IAFEgB,EAAK,eAAe,OAAO,KAAKA,EAAK,WAAW,EAAE,SAAS,KAEvCA,EAAK,cACzB,KAAK,OAAO;AAAA,MACVA,EAAK;AAAA,MACLA,EAAK;AAAA,MACLA,EAAK;AAAA,MACLA,EAAK;AAAA,MACL,OAAOhD,MACE,MAAMgD,EAAK,QAAQhD,CAAI;AAAA,IAChC,IAIF,KAAK,OAAO;AAAA,MACVgD,EAAK;AAAA,MACLA,EAAK;AAAA,MACLA,EAAK;AAAA,MACL,OAAOhD,MACE,MAAMgD,EAAK,QAAQhD,CAAI;AAAA,IAChC,GAGJ,KAAK,aAAa,cAAcgC,GAAYgB,EAAK,IAAI;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,eACXP,GACgD;AAChD,UAAME,IAAa,KAAK,SAAS,oBAAoBF,CAAW;AAChE,QAAI,CAACE,EAAW,WAAW,CAACA,EAAW,WAAW;AAChD,YAAMM,IACJ,MAAM,KAAK,KAAK,cAAc,EAAE,KAAK,IAAI,KAAK;AAEhD,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,GAHEN,EAAW,SAAS,0BAGf,qBAAqBM,CAAc;AAAA,MAAA;AAAA,IAEvD;AACA,UAAMrC,IAAY+B,EAAW;AAC7B,WAAK,KAAK,eAAe,IAAI/B,CAAS,KAUtC,KAAK,eAAe,OAAOA,CAAS,GAEpC,MAAM,KAAK,mBAAA,GAEJ;AAAA,MACL,SAAS;AAAA,MACT,SAAS,YAAYA,CAAS;AAAA,IAAA,KAfvB;AAAA,MACL,SAAS;AAAA,MACT,SAAS,YAAYA,CAAS,+CAC5B,MAAM,KAAK,KAAK,cAAc,EAAE,KAAK,IAAI,KAAK,MAChD;AAAA,IAAA;AAAA,EAaN;AAAA,EAEO,YAAY;AACjB,WAAO;AAAA,MACL,mBAAmB,KAAK,qBAAA;AAAA,MACxB,gBAAgB,KAAK,kBAAA;AAAA,MACrB,mBAAmB,CAAA;AAAA,MACnB,eAAe,KAAK,qBAAA,EAAuB;AAAA,MAC3C,aAAa,KAAK,eAAe;AAAA,MACjC,OAAO,KAAK,aAAa,KAAA;AAAA,MACzB,gBAAgB,KAAK,aAAa,cAAA;AAAA,IAAc;AAAA,EAEpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAa,eAAeC,GASzB;AACD,UAAMqC,IAKD,CAAA;AAGL,eAAW3C,KAAQM;AACjB,UAAI;AACF,cAAMsC,IAAM,MAAM,KAAK,cAAc5C,GAAM,EAAI;AAC/C,QAAA2C,EAAQ,KAAK,EAAE,MAAA3C,GAAM,GAAG4C,GAAK;AAAA,MAC/B,SAAS1B,GAAK;AACZ,QAAAyB,EAAQ,KAAK;AAAA,UACX,MAAA3C;AAAA,UACA,SAAS;AAAA,UACT,SAASkB,aAAe,QAAQA,EAAI,UAAU;AAAA,UAC9C,MAAM;AAAA,QAAA,CACP;AAAA,MACH;AAGF,UAAM2B,IAAaF,EAAQ,MAAM,CAACG,MAAMA,EAAE,OAAO,GAC3CC,IAAaJ,EAAQ,KAAK,CAACG,MAAMA,EAAE,OAAO,GAC1C1B,IAAUyB,IACZ,yBACAE,IACE,mCACA;AAGN,WAAIA,KACF,MAAM,KAAK,mBAAA,GAGN,EAAE,SAASF,GAAY,SAAAF,GAAS,SAAAvB,EAAA;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,oBASV;AACD,UAAM4B,IAAM,KAAK,qBAAA;AACjB,WAAO,KAAK,eAAeA,CAAG;AAAA,EAChC;AACF;AC3VO,MAAMC,IAAmB;AAoBzB,SAASC,GACdC,GACAC,GACAC,GACA/D,GACM;AAIN,GAHaA,GAAS,QAAQ,eAGjB,cAEX+D,EAAa,cAAcJ,GAAkB,gBAAgB,GAC7DE,EAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,EAAE,MAAMG,EAAE,SAAS,SAAS,cAAc,EAAA;AAAA,IAC1C,EAAE,iBAAiB,IAAM,gBAAgB,GAAA;AAAA,IACzC,OAAO7D,MAA2B;AAChC,YAAMM,IAAS,MAAMqD,EAAQ,cAAc3D,EAAK,IAAI;AACpD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAUM,CAAM,EAAA,CAAG;AAAA,MAAA;AAAA,IAE5D;AAAA,EAAA,GAGFsD,EAAa,cAAcJ,GAAkB,iBAAiB,GAC9DE,EAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,EAAE,MAAMG,EAAE,SAAS,SAAS,cAAc,EAAA;AAAA,IAC1C,EAAE,iBAAiB,IAAM,gBAAgB,GAAA;AAAA,IACzC,OAAO7D,MAA2B;AAChC,YAAMM,IAAS,MAAMqD,EAAQ,eAAe3D,EAAK,IAAI;AACrD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAUM,CAAM,EAAA,CAAG;AAAA,MAAA;AAAA,IAE5D;AAAA,EAAA,GAGFsD,EAAa,cAAcJ,GAAkB,eAAe,GAC5DE,EAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAA;AAAA,IACA,EAAE,cAAc,IAAM,gBAAgB,GAAA;AAAA,IACtC,YAAY;AACV,YAAMI,IAAYH,EAAQ,qBAAA,GACpBI,IAAYJ,EAAQ,UAAA,EAAY,gBAChCK,IAAQF,EAAU,IAAI,CAAC9C,MAAQ;AACnC,cAAMN,IAAMiD,EAAQ,qBAAqB3C,CAAG;AAC5C,eAAO;AAAA,UACL,KAAAA;AAAA,UACA,QAAQ2C,EAAQ,SAAS3C,CAAG;AAAA,UAC5B,YAAYN,IACR;AAAA,YACE,MAAMA,EAAI;AAAA,YACV,aAAaA,EAAI;AAAA,YACjB,SAASA,EAAI,WAAW,CAAA;AAAA,YACxB,kBAAkBA,EAAI,oBAAoB;AAAA,UAAA,IAE5C;AAAA,UACJ,OAAOqD,EAAU/C,CAAG,KAAK,CAAA;AAAA,QAAC;AAAA,MAE9B,CAAC;AACD,aAAO;AAAA,QACL,SAAS;AAAA,UACP,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,EAAE,UAAUgD,GAAO,EAAA;AAAA,QAAE;AAAA,MAC5D;AAAA,IAEJ;AAAA,EAAA,GAGFJ,EAAa,cAAcJ,GAAkB,kBAAkB,GAC/DE,EAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,EAAE,MAAMG,EAAE,SAAS,SAAS,cAAc,EAAA;AAAA,IAC1C,EAAE,cAAc,IAAM,gBAAgB,GAAA;AAAA,IACtC,OAAO7D,MAA2B;AAChC,YAAMU,IAAMiD,EAAQ,qBAAqB3D,EAAK,IAAI,GAC5C+D,IAAYJ,EAAQ,UAAA,EAAY;AACtC,UAAI,CAACjD;AACH,eAAO;AAAA,UACL,SAAS;AAAA,YACP;AAAA,cACE,MAAM;AAAA,cACN,MAAM,KAAK,UAAU,EAAE,OAAO,oBAAoBV,EAAK,IAAI,IAAA,CAAK;AAAA,YAAA;AAAA,UAClE;AAAA,QACF;AAGJ,YAAMiE,IAAU;AAAA,QACd,KAAKjE,EAAK;AAAA,QACV,QAAQ2D,EAAQ,SAAS3D,EAAK,IAAI;AAAA,QAClC,YAAY;AAAA,UACV,MAAMU,EAAI;AAAA,UACV,aAAaA,EAAI;AAAA,UACjB,SAASA,EAAI,WAAW,CAAA;AAAA,UACxB,kBAAkBA,EAAI,oBAAoB;AAAA,QAAA;AAAA,QAE5C,OAAOqD,EAAU/D,EAAK,IAAI,KAAK,CAAA;AAAA,MAAC;AAElC,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAUiE,CAAO,EAAA,CAAG;AAAA,MAAA;AAAA,IAE7D;AAAA,EAAA,IAKJL,EAAa,cAAcJ,GAAkB,YAAY,GACzDE,EAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,CAAA;AAAA,IACA,EAAE,cAAc,IAAM,gBAAgB,GAAA;AAAA,IACtC,YAAY;AACV,YAAMQ,IAASP,EAAQ,UAAA,GACjBM,IAAU;AAAA,QACd,OAAOC,EAAO;AAAA,QACd,gBAAgBA,EAAO;AAAA,MAAA;AAEzB,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAUD,CAAO,EAAA,CAAG;AAAA,MAAA;AAAA,IAE7D;AAAA,EAAA;AAEJ;ACtIO,MAAME,EAAmB;AAAA,EAQ9B,YAAYtE,GAAoC;AAP/B,IAAAC,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACT,IAAAA,EAAA,mBAA0B;AAGhC,SAAK,mBAAmB,IAAIF,GAAA;AAC5B,UAAMwE,IAAUvE,EAAQ,WAAW,CAAA,GAC7BwE,IAAW,KAAK,qBAAqBD,GAASvE,EAAQ,OAAO;AACnE,SAAK,OAAOwE,EAAS,MACrB,KAAK,WAAW,IAAIlD,GAAe;AAAA,MACjC,SAAStB,EAAQ;AAAA,MACjB,eAAeA,EAAQ;AAAA,IAAA,CACxB;AACD,UAAM+D,IAAe,IAAI7B,EAAa;AAAA,MACpC,sBACElC,EAAQ,gBAAgB,4BAA4B;AAAA,IAAA,CACvD;AACD,SAAK,UAAU,IAAI2C,GAAmB;AAAA,MACpC,QAAQ3C,EAAQ;AAAA,MAChB,UAAU,KAAK;AAAA,MACf,SAASA,EAAQ;AAAA,MACjB,oBAAoBA,EAAQ;AAAA,MAC5B,gBAAgBA,EAAQ;AAAA,MACxB,cAAA+D;AAAA,IAAA,CACD,GAGG/D,EAAQ,sBAAsB,MAChC4D,GAAkB5D,EAAQ,QAAQ,KAAK,SAAS+D,GAAc,EAAE,MAAM,KAAK,MAAM;AAInF,UAAMU,IAAUD,EAAS;AACzB,SAAK,cAAc,KAAK,mBAAmBC,CAAO;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,mBACZA,GACe;AACf,QAAI;AACF,MAAIA,MAAY,QACd,MAAM,KAAK,QAAQ,eAAe,KAAK,SAAS,sBAAsB,IAC7D,MAAM,QAAQA,CAAO,KAAKA,EAAQ,SAAS,KACpD,MAAM,KAAK,QAAQ,eAAeA,CAAO;AAAA,IAE7C,SAASxD,GAAO;AACd,WAAK,YACHA,aAAiB,QAAQA,IAAQ,IAAI,MAAM,OAAOA,CAAK,CAAC,GAC1D,QAAQ,MAAM,kCAAkC,KAAK,SAAS;AAAA,IAChE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAa,cAA6B;AAExC,QADA,MAAM,KAAK,aACP,KAAK;AACP,YAAM,KAAK;AAAA,EAEf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAa,UAA4B;AACvC,iBAAM,KAAK,aACJ,KAAK,cAAc;AAAA,EAC5B;AAAA,EAEQ,qBACNsD,GACAlE,GAC6D;AAE7D,QAAIkE,EAAQ,MAAM;AAChB,UAAIA,EAAQ,SAAS,aAAaA,EAAQ;AACxC,uBAAQ,KAAK,uDAAuD,GAC7D,EAAE,MAAM,UAAA;AAEjB,UAAIA,EAAQ,SAAS,UAAU;AAC7B,YAAIA,EAAQ,aAAa;AACvB,iBAAO,EAAE,MAAM,UAAU,UAAU,MAAA;AACrC,cAAMG,IAAQ,MAAM,QAAQH,EAAQ,QAAQ,IAAIA,EAAQ,WAAW,CAAA,GAC7D/D,IAAkB,CAAA;AACxB,mBAAWE,KAAQgE,GAAO;AACxB,gBAAM,EAAE,SAAAC,GAAS,WAAA5D,GAAW,OAAAE,EAAA,IAC1B,KAAK,iBAAiB,oBAAoBP,GAAML,CAAO;AACzD,UAAIsE,KAAW5D,IAAWP,EAAM,KAAKO,CAAS,IACrCE,KAAO,QAAQ,KAAKA,CAAK;AAAA,QACpC;AACA,YAAIyD,EAAM,SAAS,KAAKlE,EAAM,WAAW;AACvC,gBAAM,IAAI;AAAA,YACR;AAAA,UAAA;AAGJ,eAAO,EAAE,MAAM,UAAU,UAAUA,EAAA;AAAA,MACrC;AACA,aAAO,EAAE,MAAM+D,EAAQ,KAAA;AAAA,IACzB;AAGA,QAAIA,EAAQ,aAAa,MAAO,QAAO,EAAE,MAAM,UAAU,UAAU,MAAA;AACnE,QAAI,MAAM,QAAQA,EAAQ,QAAQ,KAAKA,EAAQ,SAAS,SAAS,GAAG;AAClE,YAAM/D,IAAkB,CAAA;AACxB,iBAAWE,KAAQ6D,EAAQ,UAAU;AACnC,cAAM,EAAE,SAAAI,GAAS,WAAA5D,GAAW,OAAAE,EAAA,IAC1B,KAAK,iBAAiB,oBAAoBP,GAAML,CAAO;AACzD,QAAIsE,KAAW5D,IAAWP,EAAM,KAAKO,CAAS,IACrCE,KAAO,QAAQ,KAAKA,CAAK;AAAA,MACpC;AACA,UAAIT,EAAM,WAAW;AACnB,cAAM,IAAI;AAAA,UACR;AAAA,QAAA;AAGJ,aAAO,EAAE,MAAM,UAAU,UAAUA,EAAA;AAAA,IACrC;AAGA,WAAO,EAAE,MAAM,UAAA;AAAA,EACjB;AAAA,EAEO,UAAgC;AACrC,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,aAAiC;AACtC,WAAO,KAAK;AAAA,EACd;AACF;;ACvJO,MAAMoE,EAAuB;AAAA,EAQlC,YAAY5E,IAAyC,IAAI;AARpD,IAAA6E,EAAA,MAAAC;AACG,IAAA7E,EAAA,qCAAc,IAAA;AACd,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AAEA;AAAA,IAAAA,EAAA;AAGN,SAAK,UAAUD,EAAQ,WAAW,KAClC,KAAK,QAAQA,EAAQ,SAAS,MAAO,KAAK,IAC1C,KAAK,UAAUA,EAAQ;AACvB,UAAM+E,IAAa/E,EAAQ,mBAAmB,MAAO,KAAK;AAC1D,SAAK,gBAAgB,YAAY,MAAM,KAAK,aAAA,GAAgB+E,CAAU;AAAA,EACxE;AAAA,EAEO,gBAAwB;AAC7B,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEO,aAAqB;AAC1B,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,SAAiB;AACtB,WAAO,KAAK;AAAA,EACd;AAAA,EAEO,IAAI5D,GAAuB;AAChC,UAAM6D,IAAQ,KAAK,QAAQ,IAAI7D,CAAG;AAClC,WAAK6D,IACD,KAAK,IAAA,IAAQA,EAAM,eAAe,KAAK,SACzC,KAAK,OAAO7D,CAAG,GACR,SAET6D,EAAM,eAAe,KAAK,IAAA,GAC1B,KAAK,QAAQ,OAAO7D,CAAG,GACvB,KAAK,QAAQ,IAAIA,GAAK6D,CAAK,GACpBA,EAAM,YARM;AAAA,EASrB;AAAA,EAEO,IAAI7D,GAAa8D,GAAmB;AACzC,IAAI,KAAK,QAAQ,QAAQ,KAAK,WAC5B,KAAK,uBAAA;AAEP,UAAMC,IAAqB,EAAE,UAAAD,GAAU,cAAc,KAAK,MAAI;AAC9D,SAAK,QAAQ,IAAI9D,GAAK+D,CAAQ;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOO,OAAO/D,GAAmB;AAC/B,UAAM6D,IAAQ,KAAK,QAAQ,IAAI7D,CAAG;AAClC,IAAI6D,MACF,KAAK,QAAQ,OAAO7D,CAAG,GACvBgE,EAAA,MAAKL,GAAAM,GAAL,WAAwBjE,GAAK6D,EAAM;AAAA,EAEvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,KAAKK,IAAe,IAAa;AACtC,IAAI,KAAK,kBACP,cAAc,KAAK,aAAa,GAChC,KAAK,gBAAgB,SAEnBA,KACF,KAAK,MAAA;AAAA,EAET;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,QAAc;AAEnB,UAAMC,IAAU,MAAM,KAAK,KAAK,QAAQ,SAAS;AACjD,SAAK,QAAQ,MAAA;AACb,eAAW,CAACnE,GAAK6D,CAAK,KAAKM;AACzB,MAAAH,EAAA,MAAKL,GAAAM,GAAL,WAAwBjE,GAAK6D,EAAM;AAAA,EAEvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,yBAA+B;AACrC,UAAMO,IAAS,KAAK,QAAQ,KAAA,EAAO,OAAO;AAC1C,IAAIA,KACF,KAAK,OAAOA,CAAM;AAAA,EAEtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAqB;AAC3B,UAAMC,IAAM,KAAK,IAAA,GACXC,IAAyB,CAAA;AAC/B,eAAW,CAACtE,GAAK6D,CAAK,KAAK,KAAK,QAAQ;AACtC,MAAIQ,IAAMR,EAAM,eAAe,KAAK,SAClCS,EAAa,KAAKtE,CAAG;AAIzB,eAAWA,KAAOsE;AAChB,WAAK,OAAOtE,CAAG;AAAA,EAEnB;AAsBF;AA1IO2D,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4HLM,IAAA,SAAmBjE,GAAa8D,GAAmB;AACjD,MAAK,KAAK;AACV,QAAI;AACF,YAAMxE,IAAS,KAAK,QAAQU,GAAK8D,CAAQ;AAEzC,MAAIxE,aAAkB,WACpBA,EAAO,MAAM,CAACmB,MAAQ;AACpB,gBAAQ,KAAK,6CAA6CT,CAAG,MAAMS,CAAG;AAAA,MACxE,CAAC;AAAA,IAEL,SAASA,GAAK;AACZ,cAAQ,KAAK,6CAA6CT,CAAG,MAAMS,CAAG;AAAA,IACxE;AACF;AChHK,SAAS8D,EACdC,GACAC,GACAC,GACA7F,GACM;AAEN,QAAM8F,IAAgB,CAAC,QAAQ,YAAY,UAAU,yBAAyB;AAE9E,aAAWC,KAAYF,GAAW;AAChC,UAAMG,IAAW,GAAGJ,CAAQ,GAAGG,EAAS,IAAI;AAO5C,QAJmBD,EAAc;AAAA,MAAK,CAACG,MACrCD,EAAS,WAAW,GAAGJ,CAAQ,GAAGK,CAAQ,EAAE;AAAA,IAAA,GAG9B;AACd,cAAQ;AAAA,QACN,mBAAmBF,EAAS,MAAM,IAAIA,EAAS,IAAI;AAAA,MAAA;AAErD;AAAA,IACF;AAGA,UAAMG,IAASH,EAAS,OAAO,YAAA;AAQ/B,IAAAJ,EAAIO,CAAM,EAAEF,GAAU,OAAOG,GAAqBC,MAAwB;AACxE,UAAI;AAEF,cAAMC,IAAkBF,EAAI,QAAQ,eAAe,GAAc,KAAA,GAC3DG,IACJD,KAAkBA,EAAe,SAAS,IACtCA,IACA,QAAQE,GAAY;AAG1B,YAAIC;AACJ,YAAIT,EAAS,YAAY;AACvB,gBAAMU,IAAaV,EAAS,WAAW,UAAUI,EAAI,IAAI;AACzD,cAAI,CAACM,EAAW;AACd,mBAAOC,EAAsBN,GAAO,QAAQK,EAAW,KAAK;AAE9D,UAAAD,IAAOC,EAAW;AAAA,QACpB;AAGA,YAAIE,IAAa,CAAA;AACjB,YAAIZ,EAAS,aAAa;AACxB,gBAAMa,IAAcb,EAAS,YAAY,UAAUI,EAAI,KAAK;AAC5D,cAAI,CAACS,EAAY;AACf,mBAAOF,EAAsBN,GAAO,SAASQ,EAAY,KAAK;AAEhE,UAAAD,IAAQC,EAAY;AAAA,QACtB;AAGA,YAAIC,IAAc,CAAA;AAClB,YAAId,EAAS,cAAc;AACzB,gBAAMe,IAAef,EAAS,aAAa,UAAUI,EAAI,MAAM;AAC/D,cAAI,CAACW,EAAa;AAChB,mBAAOJ,EAAsBN,GAAO,UAAUU,EAAa,KAAK;AAElE,UAAAD,IAASC,EAAa;AAAA,QACxB;AAGA,cAAMC,IAAuC;AAAA,UAC3C,MAAAP;AAAA,UACA,OAAAG;AAAA,UACA,QAAAE;AAAA,UACA,SAASV,EAAI;AAAA,UACb,UAAAG;AAAA,QAAA;AAIF,YAAItG,GAAS,kBAAkB;AAC7B,gBAAMgH,IAAoB,MAAMhH,EAAQ,iBAAiBmG,CAAG;AAC5D,iBAAO,OAAOY,GAAeC,CAAiB;AAAA,QAChD;AAGA,cAAMvG,IAAS,MAAMsF,EAAS,QAAQgB,CAAoB;AAG1D,YAAIhB,EAAS,gBAAgB;AAC3B,gBAAMkB,IAAiBlB,EAAS,eAAe,UAAUtF,CAAM;AAC/D,iBAAKwG,EAAe,UAiBbA,EAAe,QAfpB,QAAQ;AAAA,YACN,kCAAkClB,EAAS,MAAM,IAAIA,EAAS,IAAI;AAAA,YAClEkB,EAAe;AAAA,UAAA,GAIjBb,EAAM,KAAK,GAAG,GACP;AAAA,YACL,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,YAAA;AAAA,UACX;AAAA,QAKN;AAGA,eAAO3F;AAAA,MACT,SAASQ,GAAO;AAEd,uBAAQ;AAAA,UACN,4BAA4B8E,EAAS,MAAM,IAAIA,EAAS,IAAI;AAAA,UAC5D9E;AAAA,QAAA,GAGFmF,EAAM,KAAK,GAAG,GACP;AAAA,UACL,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SACEnF,aAAiB,QAAQA,EAAM,UAAU;AAAA,UAAA;AAAA,QAC7C;AAAA,MAEJ;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAYA,SAASyF,EACPN,GACAc,GACAjG,GACuB;AACvB,SAAAmF,EAAM,KAAK,GAAG,GACP;AAAA,IACL,OAAO;AAAA,MACL,MAAM;AAAA,MACN,SAAS,yBAAyBc,CAAK;AAAA,MACvC,SAASjG,EAAM;AAAA,IAAA;AAAA,EACjB;AAEJ;AC/JO,MAAMkG,GAAiB;AAAA,EA6B5B,YACEC,GACAC,GACArH,IAAmC,CAAA,GACnCsH,GACAC,GACAC,GACA;AAnCe,IAAAvH,EAAA;AASA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACT,IAAAA,EAAA,aAA8B;AACrB,IAAAA,EAAA;AAGA;AAAA,IAAAA,EAAA,qBAAc,IAAI2E,EAIhC;AAAA,MACD,SAAS,CAAC6C,GAAMC,MAAW;AAEzB,aAAK,cAAcA,CAAM;AAAA,MAC3B;AAAA,IAAA,CACD;AAUC,SAAK,iBAAiBN,GACtB,KAAK,eAAeC,GACpB,KAAK,yBAAyBE,GAC9B,KAAK,cAAcC,GACnB,KAAK,UAAU;AAAA,MACb,MAAMxH,EAAQ,QAAQ;AAAA,MACtB,MAAMA,EAAQ,QAAQ;AAAA,MACtB,UAAUA,EAAQ,YAAY;AAAA,MAC9B,MAAMA,EAAQ,QAAQ;AAAA,MACtB,QAAQA,EAAQ,UAAU;AAAA,MAC1B,KAAKA,EAAQ;AAAA,MACb,iBAAiBA,EAAQ;AAAA,IAAA,GAE3B,KAAK,eAAesH;AAAA,EACtB;AAAA,EAEA,MAAa,QAAuB;AAClC,QAAI,KAAK,IAAK;AACd,UAAM3B,IAAM,KAAK,QAAQ,OAAOgC,EAAQ,EAAE,QAAQ,KAAK,QAAQ,QAAQ;AACvE,IAAI,KAAK,QAAQ,QACf,MAAMhC,EAAI,SAASiC,GAAM,EAAE,QAAQ,IAAM;AAG3C,UAAMC,IAAO,KAAK,QAAQ,SAAS,SAAS,GAAG,IAC3C,KAAK,QAAQ,SAAS,MAAM,GAAG,EAAE,IACjC,KAAK,QAAQ;AAEjB,IAAAlC,EAAI,IAAI,GAAGkC,CAAI,YAAY,aAAa,EAAE,IAAI,KAAO,GAErDlC,EAAI,IAAI,GAAGkC,CAAI,UAAU,YAAY,KAAK,eAAe,WAAW,GAGpElC,EAAI,IAAI,GAAGkC,CAAI,2BAA2B,OAAOC,GAAM1B,OACrDA,EAAM,OAAO,gBAAgB,wCAAwC,GAClD,KAAK,gBAAgB;AAAA,MACtC,SAAS;AAAA,MACT,OAAO;AAAA,MACP,aAAa;AAAA,MACb,MAAM;AAAA,MACN,YAAY,CAAA;AAAA,MACZ,UAAU,CAAA;AAAA,MACV,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,IAAA,EAGpB,GAGDT,EAAI;AAAA,MACF,GAAGkC,CAAI;AAAA,MACP,OAAO1B,GAAqBC,MAAwB;AAClD,cAAMC,IACJF,EAAI,QAAQ,eAAe,GAC1B,KAAA,GACGG,IACJD,KAAkBA,EAAe,SAAS,IACtCA,IACA,QAAQE,GAAY,IAGpBwB,IAAW,CAACzB,EAAS,WAAW,OAAO,GAGvC,EAAE,UAAA0B,GAAU,eAAAC,EAAA,IAAkB,KAAK;AAAA,UACvC9B;AAAA,UACAG;AAAA,QAAA;AAGF,YAAIoB,IAASK,IAAW,KAAK,YAAY,IAAIC,CAAQ,IAAI;AACzD,YAAI,CAACN,GAAQ;AACX,gBAAMQ,IAAU,KAAK,aAAaD,CAAa;AAC/C,UAAAP,IAAS;AAAA,YACP,QAAQQ,EAAQ;AAAA,YAChB,cAAcA,EAAQ;AAAA,YACtB,8BAAc,IAAA;AAAA,UAAI,GAEhBH,KAAU,KAAK,YAAY,IAAIC,GAAUN,CAAM;AAAA,QACrD;AAEA,cAAMS,IAAYhC,EAAI,QAAQ,gBAAgB;AAE9C,YAAIiC;AACJ,YAAID,KAAaT,EAAO,SAAS,IAAIS,CAAS;AAC5C,UAAAC,IAAYV,EAAO,SAAS,IAAIS,CAAS;AAAA,iBAChC,CAACA,KAAaE,EAAqBlC,EAAY,IAAI,GAAG;AAC/D,gBAAMmC,IAAe/B,EAAA;AACrB,UAAA6B,IAAY,IAAIG,EAA8B;AAAA,YAC5C,oBAAoB,MAAMD;AAAA,YAC1B,sBAAsB,CAACE,MAAgB;AACrC,cAAAd,EAAQ,SAAS,IAAIc,GAAKJ,CAAU;AAAA,YACtC;AAAA,UAAA,CACD;AACD,cAAI;AACF,kBAAMV,EAAO,OAAO,QAAQU,CAAS;AAAA,UACvC,QAAgB;AACd,mBAAAhC,EAAM,KAAK,GAAG,GACP;AAAA,cACL,SAAS;AAAA,cACT,OAAO,EAAE,MAAM,QAAQ,SAAS,6BAAA;AAAA,cAChC,IAAI;AAAA,YAAA;AAAA,UAER;AACA,UAAAgC,EAAU,UAAU,MAAM;AACxB,YAAIA,GAAW,aACbV,EAAQ,SAAS,OAAOU,EAAU,SAAS;AAAA,UAC/C;AAAA,QACF;AACE,iBAAAhC,EAAM,KAAK,GAAG,GACP;AAAA,YACL,SAAS;AAAA,YACT,OAAO,EAAE,MAAM,OAAQ,SAAS,+BAAA;AAAA,YAChC,IAAI;AAAA,UAAA;AAKR,qBAAMgC,EAAU;AAAA,UACbjC,EAAY;AAAA,UACZC,EAAc;AAAA,UACdD,EAAY;AAAA,QAAA,GAGRC;AAAA,MACT;AAAA,IAAA,GAIFT,EAAI,IAAI,GAAGkC,CAAI,QAAQ,OAAO1B,GAAqBC,MAAwB;AACzE,YAAMC,IACJF,EAAI,QAAQ,eAAe,GAC1B,KAAA,GACGG,IACJD,KAAkBA,EAAe,SAAS,IAAIA,IAAiB;AACjE,UAAI,CAACC;AACH,eAAAF,EAAM,KAAK,GAAG,GACP;AAET,YAAMsB,IAAS,KAAK,YAAY,IAAIpB,CAAQ;AAC5C,UAAI,CAACoB;AACH,eAAAtB,EAAM,KAAK,GAAG,GACP;AAET,YAAM+B,IAAYhC,EAAI,QAAQ,gBAAgB;AAC9C,UAAI,CAACgC;AACH,eAAA/B,EAAM,KAAK,GAAG,GACP;AAET,YAAMgC,IAAYV,EAAO,SAAS,IAAIS,CAAS;AAC/C,aAAKC,KAIL,MAAMA,EAAU,cAAejC,EAAY,KAAMC,EAAc,GAAG,GAC3DA,MAJLA,EAAM,KAAK,GAAG,GACP;AAAA,IAIX,CAAC,GAGDT,EAAI;AAAA,MACF,GAAGkC,CAAI;AAAA,MACP,OAAO1B,GAAqBC,MAAwB;AAClD,cAAMC,IACJF,EAAI,QAAQ,eAAe,GAC1B,KAAA,GACGG,IACJD,KAAkBA,EAAe,SAAS,IAAIA,IAAiB,IAC3D8B,IAAYhC,EAAI,QAAQ,gBAAgB;AAC9C,YAAI,CAACG,KAAY,CAAC6B;AAChB,iBAAA/B,EAAM,KAAK,GAAG,GACP;AAAA,YACL,SAAS;AAAA,YACT,OAAO;AAAA,cACL,MAAM;AAAA,cACN,SAAS;AAAA,YAAA;AAAA,YAEX,IAAI;AAAA,UAAA;AAGR,cAAMsB,IAAS,KAAK,YAAY,IAAIpB,CAAQ,GACtC8B,IAAYV,GAAQ,SAAS,IAAIS,CAAS;AAChD,YAAI,CAACT,KAAU,CAACU;AACd,iBAAAhC,EAAM,KAAK,GAAG,GACP;AAAA,YACL,SAAS;AAAA,YACT,OAAO,EAAE,MAAM,OAAQ,SAAS,+BAAA;AAAA,YAChC,IAAI;AAAA,UAAA;AAGR,YAAI;AAEF,cAAI,OAAQgC,EAAkB,SAAU;AACtC,gBAAI;AACF,oBAAOA,EAAkB,MAAA;AAAA,YAC3B,QAAQ;AAAA,YAAC;AAAA,QAEb,UAAA;AACE,UAAIA,GAAW,YAAWV,EAAO,SAAS,OAAOU,EAAU,SAAS,IAC/DV,EAAO,SAAS,OAAOS,CAAS;AAAA,QACvC;AACA,eAAA/B,EAAM,KAAK,GAAG,EAAE,KAAA,GACTA;AAAA,MACT;AAAA,IAAA,GAKE,KAAK,QAAQ,mBAAmB,KAAK,QAAQ,gBAAgB,SAAS,KACxEV,EAAwBC,GAAKkC,GAAM,KAAK,QAAQ,eAAe,GAI5D,KAAK,QAAQ,OAChB,MAAMlC,EAAI,OAAO,EAAE,MAAM,KAAK,QAAQ,MAAM,MAAM,KAAK,QAAQ,KAAA,CAAM,GAEvE,KAAK,MAAMA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,OAAsB;AACjC,IAAK,KAAK,QAGV,KAAK,YAAY,KAAK,EAAI,GAErB,KAAK,QAAQ,OAChB,MAAM,KAAK,IAAI,MAAA,GAEjB,KAAK,MAAM;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,cAAc+B,GAIb;AACP,eAAW,CAACS,GAAWC,CAAS,KAAKV,EAAO,SAAS;AACnD,UAAI;AACF,QAAI,OAAQU,EAAkB,SAAU,cACrCA,EAAkB,MAAA,EAAQ,MAAM,CAACxG,MAAiB;AACjD,kBAAQ,KAAK,yBAAyBuG,CAAS,KAAKvG,CAAG;AAAA,QACzD,CAAC;AAAA,MAEL,SAASA,GAAK;AACZ,gBAAQ,KAAK,yBAAyBuG,CAAS,KAAKvG,CAAG;AAAA,MACzD;AAEF,IAAA8F,EAAO,SAAS,MAAA;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,sBACNvB,GACAG,GAC8C;AAE9C,QAAI,CAAC,KAAK;AACR,aAAO;AAAA,QACL,UAAUA;AAAA,QACV,eAAe,KAAK;AAAA,MAAA;AAKxB,UAAMmC,IAA+C;AAAA,MACnD,UAAAnC;AAAA,MACA,SAAS,KAAK,eAAeH,CAAG;AAAA,MAChC,OAAO,KAAK,aAAaA,CAAG;AAAA,IAAA,GAIxB1F,IAAS,KAAK,uBAAuB;AAAA,MACzCgI;AAAA,MACA,KAAK;AAAA,IAAA;AASP,WAAO;AAAA,MACL,UALAhI,EAAO,mBAAmB,YACtB6F,IACA,GAAGA,CAAQ,IAAI7F,EAAO,cAAc;AAAA,MAIxC,eAAeA,EAAO;AAAA,IAAA;AAAA,EAE1B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,eAAe0F,GAA6C;AAClE,UAAMuC,IAAkC,CAAA;AACxC,eAAW,CAACvH,GAAKC,CAAK,KAAK,OAAO,QAAQ+E,EAAI,OAAO;AACnD,MAAI,OAAO/E,KAAU,WACnBsH,EAAQvH,EAAI,YAAA,CAAa,IAAIC,IACpB,MAAM,QAAQA,CAAK,KAAKA,EAAM,SAAS,MAChDsH,EAAQvH,EAAI,YAAA,CAAa,IAAIC,EAAM,CAAC;AAGxC,WAAOsH;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,aAAavC,GAA6C;AAChE,UAAMQ,IAAgC,CAAA,GAChCgC,IAAWxC,EAAI;AACrB,QAAIwC,KAAY,OAAOA,KAAa;AAClC,iBAAW,CAACxH,GAAKC,CAAK,KAAK,OAAO,QAAQuH,CAAQ;AAChD,QAAI,OAAOvH,KAAU,aACnBuF,EAAMxF,CAAG,IAAIC;AAInB,WAAOuF;AAAA,EACT;AACF;AC3WO,MAAMiC,GAAuB;AAAA,EAOlC,YAAYC,GAA8B;AANzB,IAAA5I,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AACA,IAAAA,EAAA;AAGf,SAAK,SAAS4I,GACd,KAAK,iBAAiBA,EAAO,YAAY,QAAQ,UACjD,KAAK,WAAWA,EAAO,YAAY,YAAY,UAC/C,KAAK,cAAcA,EAAO,YAAY,cAClC,IAAI,IAAIA,EAAO,WAAW,WAAW,IACrC,MACJ,KAAK,gBAAgBA,EAAO,SAAS;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,QACEC,GACAtB,GACsB;AAEtB,QAAI,KAAK,OAAO,YAAY;AAC1B,aAAO;AAAA,QACL,SAASA;AAAA,QACT,gBAAgB;AAAA,MAAA;AAKpB,UAAMuB,IAAe,KAAK,iBAAiBD,EAAQ,KAAK;AAGxD,QAAI,KAAK,OAAO;AACd,UAAI;AAMF,eAAO;AAAA,UACL,SANsB,KAAK,OAAO;AAAA,YAClCA;AAAA,YACAtB;AAAA,YACAuB;AAAA,UAAA;AAAA,UAIA,gBAAgB,KAAK,uBAAuBA,CAAY;AAAA,QAAA;AAAA,MAE5D,QAAQ;AAEN,eAAO;AAAA,UACL,SAASvB;AAAA,UACT,gBAAgB;AAAA,QAAA;AAAA,MAEpB;AAKF,WAAO;AAAA,MACL,SAFoB,KAAK,cAAcA,GAAauB,CAAY;AAAA,MAGhE,gBAAgB,KAAK,uBAAuBA,CAAY;AAAA,IAAA;AAAA,EAE5D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,iBACNpC,GACyB;AACzB,UAAMqC,IAAWrC,EAAM,KAAK,cAAc;AAC1C,QAAI,CAACqC;AACH,aAAO,CAAA;AAGT,QAAI;AACF,UAAIC;AAEJ,MAAI,KAAK,aAAa,WAEpBA,IAAa,OAAO,KAAKD,GAAU,QAAQ,EAAE,SAAS,OAAO,IAG7DC,IAAaD;AAGf,YAAME,IAAS,KAAK,MAAMD,CAAU;AAGpC,aAAI,OAAOC,KAAW,YAAYA,MAAW,QAAQ,MAAM,QAAQA,CAAM,IAChE,CAAA,IAIF,KAAK,kBAAkBA,CAAM;AAAA,IACtC,QAAQ;AAEN,aAAO,CAAA;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,kBACNA,GACyB;AACzB,QAAI,CAAC,KAAK;AACR,aAAOA;AAGT,UAAMC,IAAoC,CAAA;AAC1C,eAAWhI,KAAO,KAAK;AACrB,MAAIA,KAAO+H,MACTC,EAAShI,CAAG,IAAI+H,EAAO/H,CAAG;AAG9B,WAAOgI;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,cACN3B,GACA4B,GACS;AAET,WAAI,OAAO,KAAKA,CAAa,EAAE,WAAW,IACjC5B,IAKP,OAAOA,KAAgB,YACvBA,MAAgB,QAChB,MAAM,QAAQA,CAAW,IAElB4B,IAGL,KAAK,kBAAkB,SAClB,KAAK;AAAA,MACV5B;AAAA,MACA4B;AAAA,IAAA,IAKG;AAAA,MACL,GAAI5B;AAAA,MACJ,GAAG4B;AAAA,IAAA;AAAA,EAEP;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWQ,UACNvB,GACAwB,GACyB;AACzB,UAAM5I,IAAkC,EAAE,GAAGoH,EAAA;AAE7C,eAAW,CAAC1G,GAAKC,CAAK,KAAK,OAAO,QAAQiI,CAAQ,GAAG;AACnD,YAAMC,IAAY7I,EAAOU,CAAG;AAE5B,MACE,OAAOC,KAAU,YACjBA,MAAU,QACV,CAAC,MAAM,QAAQA,CAAK,KACpB,OAAOkI,KAAc,YACrBA,MAAc,QACd,CAAC,MAAM,QAAQA,CAAS,IAGxB7I,EAAOU,CAAG,IAAI,KAAK;AAAA,QACjBmI;AAAA,QACAlI;AAAA,MAAA,IAIFX,EAAOU,CAAG,IAAIC;AAAA,IAElB;AAEA,WAAOX;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,uBACN2I,GACQ;AACR,QAAI,OAAO,KAAKA,CAAa,EAAE,WAAW;AACxC,aAAO;AAIT,UAAMG,IAAa,OAAO,KAAKH,CAAa,EAAE,KAAA,GACxCI,IAAyC,CAAA;AAC/C,eAAWrI,KAAOoI;AAChB,MAAAC,EAAcrI,CAAG,IAAIiI,EAAcjI,CAAG;AAGxC,UAAM8H,IAAa,KAAK,UAAUO,CAAa;AAC/C,WAAOC,GAAW,QAAQ,EAAE,OAAOR,CAAU,EAAE,OAAO,KAAK,EAAE,MAAM,GAAG,EAAE;AAAA,EAC1E;AACF;ACjSO,SAASS,EAA6Bb,GAAoC;AAC/Ec,EAAAA,GAAqBd,CAAM,GAC3Be,GAAqBf,CAAM,GAC3BgB,GAAyBhB,CAAM,GAC/BiB,GAAwBjB,CAAM,GAC9BkB,GAAsBlB,CAAM;AAC9B;AASA,SAASc,GAAqBd,GAAoC;AAChE,MAAI,CAACA,KAAU,OAAOA,KAAW;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAGN;AASA,SAASe,GAAqBf,GAAoC;AAChE,MAAIA,EAAO,YAAY,UAInB,OAAOA,EAAO,WAAY;AAC5B,UAAM,IAAI;AAAA,MACR,kCAAkC,OAAOA,EAAO,OAAO;AAAA,IAAA;AAG7D;AASA,SAASgB,GAAyBhB,GAAoC;AACpE,MAAIA,EAAO,eAAe,QAI1B;AAAA,QAAI,OAAOA,EAAO,cAAe,YAAYA,EAAO,eAAe;AACjE,YAAM,IAAI,MAAM,8BAA8B;AAIhD,QAAIA,EAAO,WAAW,SAAS,WAE3B,OAAOA,EAAO,WAAW,QAAS,YAClCA,EAAO,WAAW,KAAK,WAAW;AAElC,YAAM,IAAI,MAAM,4CAA4C;AAKhE,QAAIA,EAAO,WAAW,aAAa,UAE/BA,EAAO,WAAW,aAAa,YAC/BA,EAAO,WAAW,aAAa;AAE/B,YAAM,IAAI;AAAA,QACR,iCAAiCA,EAAO,WAAW,QAAQ;AAAA,MAAA;AAMjE,QAAIA,EAAO,WAAW,gBAAgB,QAAW;AAC/C,UAAI,CAAC,MAAM,QAAQA,EAAO,WAAW,WAAW;AAC9C,cAAM,IAAI,MAAM,oDAAoD;AAGtE,eAASmB,IAAI,GAAGA,IAAInB,EAAO,WAAW,YAAY,QAAQmB,KAAK;AAC7D,cAAM7I,IAAM0H,EAAO,WAAW,YAAYmB,CAAC;AAC3C,YAAI,OAAO7I,KAAQ,YAAYA,EAAI,WAAW;AAC5C,gBAAM,IAAI;AAAA,YACR,0BAA0B6I,CAAC;AAAA,UAAA;AAAA,MAGjC;AAAA,IACF;AAAA;AACF;AASA,SAASF,GAAwBjB,GAAoC;AACnE,MAAIA,EAAO,oBAAoB,UAI3B,OAAOA,EAAO,mBAAoB;AACpC,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAGN;AASA,SAASkB,GAAsBlB,GAAoC;AACjE,MAAIA,EAAO,UAAU,UAIjBA,EAAO,UAAU,aAAaA,EAAO,UAAU;AACjD,UAAM,IAAI;AAAA,MACR,4BAA4BA,EAAO,KAAK;AAAA,IAAA;AAG9C;ACrFA,MAAMoB,KAAsBjG,EACzB,OAAO;AAAA,EACN,MAAMA,EAAE,KAAK,CAAC,WAAW,QAAQ,CAAC,EAAE,SAAA;AAAA,EACpC,UAAUA,EAAE,MAAM,CAACA,EAAE,MAAMA,EAAE,OAAA,CAAQ,GAAGA,EAAE,QAAQ,KAAK,CAAC,CAAC,EAAE,SAAA;AAC7D,CAAC,EACA,OAAA;AAEH,eAAsBkG,GAAgBlK,GAAiC;AAErE,MAAIA,EAAQ;AACV,QAAI;AACF,MAAAiK,GAAoB,MAAMjK,EAAQ,OAAO;AAAA,IAC3C,SAASiB,GAAO;AACd,UAAIA,aAAiB+C,EAAE,UAAU;AAC/B,cAAMmG,IAAYlJ,EAAM,OAAA;AACxB,cAAM,IAAI;AAAA,UACR;AAAA,EAAmC,KAAK,UAAUkJ,GAAW,MAAM,CAAC,CAAC;AAAA;AAAA;AAAA,QAAA;AAAA,MAGzE;AACA,YAAMlJ;AAAA,IACR;AAGF,QAAMmJ,IAA6BpK,EAAQ,SAAS,QAAQ;AAG5D,MAAIuH;AAaJ,MAZIvH,EAAQ,mBACV0J,EAA6B1J,EAAQ,cAAc,GACnDuH,IAAyB,IAAIqB,GAAuB5I,EAAQ,cAAc,GAGtEoK,MAAS,YAAYpK,EAAQ,eAAe,YAAY,MAC1D,QAAQ;AAAA,IACN;AAAA,EAAA,IAKF,OAAOA,EAAQ,gBAAiB;AAClC,UAAM,IAAI,MAAM,uDAAuD;AAEzE,QAAMqK,IAAwBrK,EAAQ,aAAA,GAOhCsK,IAAe,CAAC/J,MACpB,OAAQA,GAAiB,QAAQ,gBAAiB,YAC9CgK,IAAe,CAAChK,MACpB,OAAQA,GAAiB,0BAA2B,YAQhDiK,IAAqB,OAAOC,MAAoB;AACpD,QAAI;AACF,UAAIH,EAAaG,CAAM,GAAG;AACxB,cAAMA,EAAO,OAAO,aAAa;AAAA,UAC/B,QAAQ;AAAA,QAAA,CACT;AACD;AAAA,MACF;AACA,MAAIF,EAAaE,CAAM,KACrB,MAAMA,EAAO,uBAAA;AAAA,IAEjB,SAAS7I,GAAK;AAGZ,WADqBA,aAAe,QAAQA,EAAI,UAAU,OAAOA,CAAG,OAC/C;AACnB;AAGF,cAAQ,KAAK,mDAAmDA,CAAG;AAAA,IACrE;AAAA,EACF,GAEM8I,IAAe,IAAIpG,EAAmB;AAAA,IAC1C,QAAQ+F;AAAA,IACR,SAASrK,EAAQ;AAAA,IACjB,eAAeA,EAAQ;AAAA,IACvB,gBAAgBA,EAAQ;AAAA,IACxB,SAASA,EAAQ;AAAA,IACjB,wBAAwB,YAAYwK,EAAmBH,CAAU;AAAA,IACjE,SAASrK,EAAQ;AAAA,IACjB,mBACEA,EAAQ,sBAAsB,SAC1BA,EAAQ,oBACRoK,MAAS;AAAA,EAAA,CAChB;AAGD,EAAIA,MAAS,YACX,MAAMM,EAAa,YAAA;AAGrB,QAAMtC,IAAY,IAAIjB;AAAA,IACpBuD,EAAa,WAAA;AAAA,IACb,CAACzC,MAA4B;AAK3B,YAAM0C,IAAmB1C,KAAiBjI,EAAQ;AAElD,UAAIoK,MAAS;AAEX,eAAO,EAAE,QAAQC,GAAY,cAAAK,EAAA;AAE/B,YAAME,IAA2B5K,EAAQ,aAAA,GACnC6K,IAAsB,IAAIvG,EAAmB;AAAA,QACjD,QAAQsG;AAAA,QACR,SAAS5K,EAAQ;AAAA,QACjB,eAAeA,EAAQ;AAAA,QACvB,gBAAgBA,EAAQ;AAAA,QACxB,SAAS2K;AAAA,QACT,wBAAwB,YAAYH,EAAmBI,CAAa;AAAA,QACpE,SAAS5K,EAAQ;AAAA,QACjB,mBACEA,EAAQ,sBAAsB,SAC1BA,EAAQ,oBACRoK,MAAS;AAAA,MAAA,CAChB;AACD,aAAO,EAAE,QAAQQ,GAAe,cAAcC,EAAA;AAAA,IAChD;AAAA,IACA7K,EAAQ;AAAA,IACRA,EAAQ;AAAA,IACRuH;AAAA,IACAvH,EAAQ;AAAA,EAAA;AAGV,SAAO;AAAA,IACL,QAAQqK;AAAA,IACR,OAAO,YAAY;AACjB,YAAMjC,EAAU,MAAA;AAAA,IAClB;AAAA,IACA,OAAO,YAAY;AACjB,YAAMA,EAAU,KAAA;AAAA,IAClB;AAAA,EAAA;AAEJ;ACnMO,SAAS0C,GAAyBjC,GAAgC;AACvE,EAAAc,GAAqBd,CAAM,GAC3BkC,GAAoBlC,CAAM,GAC1BmC,GAA+BnC,CAAM,GACrCoC,GAAcpC,CAAM;AACtB;AAQA,SAASc,GAAqBd,GAAgC;AAC5D,MAAI,CAACA,KAAU,OAAOA,KAAW;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAGN;AAQA,SAASkC,GAAoBlC,GAAgC;AAC3D,MAAI,CAACA,EAAO;AACV,UAAM,IAAI,MAAM,wDAAwD;AAG1E,MAAIA,EAAO,WAAW,aAAaA,EAAO,WAAW;AACnD,UAAM,IAAI;AAAA,MACR,+BAA+BA,EAAO,MAAM;AAAA,IAAA;AAGlD;AASA,SAASmC,GAA+BnC,GAAgC;AACtE,MAAIA,EAAO,WAAW,YAChB,CAACA,EAAO,aAAa,CAACA,EAAO;AAC/B,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAIR;AASA,SAASoC,GAAcpC,GAAgC;AACrD,MAAIA,EAAO,cAAc,QAAW;AAClC,QAAI,OAAOA,EAAO,aAAc,YAAYA,EAAO,cAAc;AAC/D,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAKJ,IAAAqC,GAAwBrC,EAAO,SAAS;AAAA,EAC1C;AAEA,MAAIA,EAAO,aAAa,UAClB,OAAOA,EAAO,YAAa;AAC7B,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAKN,MAAIA,EAAO,uBAAuB,UAC5B,CAAC,MAAM,QAAQA,EAAO,kBAAkB;AAC1C,UAAM,IAAI,MAAM,sDAAsD;AAI1E,MAAIA,EAAO,eAAe,WACpB,OAAOA,EAAO,cAAe,YAAYA,EAAO,WAAW,WAAW;AACxE,UAAM,IAAI,MAAM,uCAAuC;AAG7D;AAQA,SAASqC,GAAwBC,GAA2C;AAC1E,aAAW,CAAC7E,GAAU8E,CAAW,KAAK,OAAO,QAAQD,CAAS;AAC5D,QAAI,CAAC,MAAM,QAAQC,CAAW;AAC5B,YAAM,IAAI;AAAA,QACR,+BAA+B9E,CAAQ;AAAA,MAAA;AAI/C;;AC/GO,MAAM+E,GAAmB;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ9B,YAAoBxC,GAA0B;AARzC,IAAAhE,EAAA,MAAAyG;AACG,IAAArL,EAAA,mCAAY,IAAA;AACH,IAAAA,EAAA;AAMG,SAAA,SAAA4I,GAElB,KAAK,wBACHA,EAAO,cAAc,2BACrB,YAAA;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeA,mBACEvC,GACAoC,GACU;AAEV,QAAI,KAAK,MAAM,IAAIpC,CAAQ;AACzB,aAAO,KAAK,MAAM,IAAIA,CAAQ;AAGhC,QAAI8E;AAEJ,QAAI;AACF,MAAI,KAAK,OAAO,WAAW,YACzBA,IAAcjG,EAAA,MAAKmG,GAAAC,GAAL,WAA6B7C,KAE3C0C,IAAcjG,EAAA,MAAKmG,GAAAE,GAAL,WAA+BlF,IAI1C,MAAM,QAAQ8E,CAAW,MAC5B,QAAQ;AAAA,QACN,uDAAuD9E,CAAQ;AAAA,MAAA,GAEjE8E,IAAc,CAAA,IAIhBA,IAAcA,EAAY;AAAA,QACxB,CAAC1K,MAAS,OAAOA,KAAS,YAAYA,EAAK,KAAA,EAAO,SAAS;AAAA,MAAA;AAAA,IAE/D,SAASO,GAAO;AAEd,cAAQ;AAAA,QACN,qDAAqDqF,CAAQ;AAAA,QAC7DrF;AAAA,MAAA,GAEFmK,IAAc,CAAA;AAAA,IAChB;AAGA,gBAAK,MAAM,IAAI9E,GAAU8E,CAAW,GAC7BA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBAAgB9E,GAAwB;AACtC,SAAK,MAAM,OAAOA,CAAQ;AAAA,EAC5B;AAAA;AAAA;AAAA;AAAA;AAAA,EA+IA,aAAmB;AACjB,SAAK,MAAM,MAAA;AAAA,EACb;AACF;AAjOOgF,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0FLC,aAAwB7C,GAA4C;AAClE,MAAI,CAACA;AACH,WAAO,CAAA;AAIT,QAAM+C,IAActG,EAAA,MAAKmG,GAAAI,GAAL,WAClBhD,GACA,KAAK;AAGP,MAAI,CAAC+C;AACH,WAAO,CAAA;AAGT,MAAI;AAEF,WAAOA,EACJ,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAA,CAAM,EACnB,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC;AAAA,EAC/B,SAASxK,GAAO;AAEd,mBAAQ;AAAA,MACN,sCAAsC,KAAK,oBAAoB;AAAA,MAC/DA;AAAA,IAAA,GAEK,CAAA;AAAA,EACT;AACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUAyK,IAAA,SACEhD,GACAiD,GACoB;AAEpB,MAAIjD,EAAQiD,CAAa,MAAM;AAC7B,WAAOjD,EAAQiD,CAAa;AAG9B,aAAW,CAACxK,GAAKC,CAAK,KAAK,OAAO,QAAQsH,CAAO;AAC/C,QAAIvH,EAAI,YAAA,MAAkBwK;AACxB,aAAOvK;AAIb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUAoK,aAA0BlF,GAA4B;AAEpD,MAAI,KAAK,OAAO,UAAU;AACxB,UAAMsF,IAAiBzG,EAAA,MAAKmG,GAAAO,GAAL,WAA0BvF;AACjD,QAAIsF,MAAmB;AACrB,aAAOA;AAAA,EAGX;AAGA,MAAI,KAAK,OAAO,WAAW;AACzB,UAAME,IAAe3G,EAAA,MAAKmG,GAAAS,GAAL,WAAsBzF;AAC3C,QAAIwF,MAAiB;AACnB,aAAOA;AAAA,EAEX;AAGA,SAAO,KAAK,OAAO,sBAAsB,CAAA;AAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASAD,aAAqBvF,GAAmC;AACtD,MAAI;AACF,UAAM7F,IAAS,KAAK,OAAO,SAAU6F,CAAQ;AAC7C,WAAI,MAAM,QAAQ7F,CAAM,IACfA,KAET,QAAQ;AAAA,MACN,qDAAqD6F,CAAQ;AAAA,IAAA,GAExD;AAAA,EACT,SAASrF,GAAO;AAEd,UAAMa,IAAUb,aAAiB,QAAQA,EAAM,UAAU,OAAOA,CAAK;AACrE,mBAAQ;AAAA,MACN,uCAAuCqF,CAAQ,KAAKxE,CAAO;AAAA,IAAA,GAEtD;AAAA,EACT;AACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASAiK,aAAiBzF,GAAmC;AAClD,QAAM8E,IAAc,KAAK,OAAO,UAAW9E,CAAQ;AACnD,SAAI8E,MAAgB,SACX,MAAM,QAAQA,CAAW,IAAIA,IAAc,CAAA,IAE7C;AACT;ACnKK,SAASY,GACdC,GAIAC,GACA;AAYA,SAAO,OACL3K,MACmC;AAEnC,UAAM4K,IAAoBD,EAAmB;AAAA,MAC3C3K,EAAQ;AAAA,MACRA,EAAQ;AAAA,IAAA,GAIJmG,IAASuE,EAAqBE,CAAiB,GAI/CrI,IAAU4D,EAAO,aAAa,WAAA,GAE9B0E,IAA4B,CAAA,GAC5BC,IAA2B,CAAA;AAEjC,QAAIF,EAAkB,SAAS,GAAG;AAChC,YAAM1L,IAAS,MAAMqD,EAAQ,eAAeqI,CAAiB;AAG7D,iBAAW3I,KAAK/C,EAAO;AACrB,QAAI+C,EAAE,UACJ4I,EAAgB,KAAK5I,EAAE,IAAI,KAE3B6I,EAAe,KAAK7I,EAAE,IAAI,GAC1B,QAAQ;AAAA,UACN,6BAA6BA,EAAE,IAAI,iBAAiBjC,EAAQ,QAAQ,MAAMiC,EAAE,OAAO;AAAA,QAAA;AAMzF,UAAI4I,EAAgB,WAAW,KAAKC,EAAe,SAAS;AAC1D,cAAM,IAAI;AAAA,UACR,uDAAuD9K,EAAQ,QAAQ,kBACtD4K,EAAkB,KAAK,IAAI,CAAC;AAAA,QAAA;AAAA,IAInD;AAGA,WAAO;AAAA,MACL,QAAQzE,EAAO;AAAA,MACf,cAAcA,EAAO;AAAA,MACrB,iBAAiB0E;AAAA,MACjB,gBAAAC;AAAA,IAAA;AAAA,EAEJ;AACF;;ACvFO,MAAMC,GAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsC3C,YACElF,GACA4E,GAGAhM,IAAkD,CAAA,GAClDsH,GACA;AA7CG,IAAAzC,EAAA,MAAA0H;AACY,IAAAtM,EAAA;AASA,IAAAA,EAAA;AACA,IAAAA,EAAA;AAGT,IAAAA,EAAA,aAA8B;AACrB,IAAAA,EAAA;AAGA;AAAA,IAAAA,EAAA,qBAAc,IAAI2E,EAMhC;AAAA,MACD,SAAS,CAAC6C,GAAMC,MAAW;AAEzB,QAAAvC,EAAA,MAAKoH,GAAAC,GAAL,WAAoB9E;AAAA,MACtB;AAAA,IAAA,CACD;AAiBC,SAAK,iBAAiBN,GACtB,KAAK,8BAA8B4E,GACnC,KAAK,UAAU;AAAA,MACb,MAAMhM,EAAQ,QAAQ;AAAA,MACtB,MAAMA,EAAQ,QAAQ;AAAA,MACtB,UAAUA,EAAQ,YAAY;AAAA,MAC9B,MAAMA,EAAQ,QAAQ;AAAA,MACtB,QAAQA,EAAQ,UAAU;AAAA,MAC1B,KAAKA,EAAQ;AAAA,MACb,iBAAiBA,EAAQ;AAAA,IAAA,GAE3B,KAAK,eAAesH;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,QAAuB;AAClC,QAAI,KAAK,IAAK;AACd,UAAM3B,IAAM,KAAK,QAAQ,OAAOgC,EAAQ,EAAE,QAAQ,KAAK,QAAQ,QAAQ;AACvE,IAAI,KAAK,QAAQ,QACf,MAAMhC,EAAI,SAASiC,GAAM,EAAE,QAAQ,IAAM;AAG3C,UAAMC,IAAO1C,EAAA,MAAKoH,GAAAE,GAAL,WAAwB,KAAK,QAAQ;AAElD,IAAAtH,EAAA,MAAKoH,GAAAG,GAAL,WAA6B/G,GAAKkC,IAClC1C,EAAA,MAAKoH,GAAAI,GAAL,WAA4BhH,GAAKkC,IACjC1C,EAAA,MAAKoH,GAAAK,GAAL,WAAsCjH,GAAKkC,IAC3C1C,EAAA,MAAKoH,GAAAM,IAAL,WAA8BlH,GAAKkC,IACnC1C,EAAA,MAAKoH,GAAAO,IAAL,WAA6BnH,GAAKkC,IAClC1C,EAAA,MAAKoH,GAAAQ,IAAL,WAAgCpH,GAAKkC,IAIjC,KAAK,QAAQ,mBAAmB,KAAK,QAAQ,gBAAgB,SAAS,KACxEnC,EAAwBC,GAAKkC,GAAM,KAAK,QAAQ,iBAAiB;AAAA,MAC/D,kBAAkB,OAAO1B,MAAQ;AAE/B,cAAM5E,IAAU4D,EAAA,MAAKoH,GAAAS,GAAL,WAA2B7G;AAG3C,YAAI;AACF,gBAAMuB,IAAS,MAAM,KAAK,4BAA4BnG,CAAO;AAC7D,iBAAO;AAAA,YACL,iBAAiBmG,EAAO;AAAA,YACxB,gBAAgBA,EAAO;AAAA,UAAA;AAAA,QAE3B,SAASzG,GAAO;AAEd,yBAAQ;AAAA,YACN,qDAAqDA,CAAK;AAAA,UAAA,GAErD;AAAA,YACL,iBAAiB,CAAA;AAAA,YACjB,gBAAgB,CAAA;AAAA,UAAC;AAAA,QAErB;AAAA,MACF;AAAA,IAAA,CACD,GAIE,KAAK,QAAQ,OAChB,MAAM0E,EAAI,OAAO,EAAE,MAAM,KAAK,QAAQ,MAAM,MAAM,KAAK,QAAQ,KAAA,CAAM,GAEvE,KAAK,MAAMA;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAa,OAAsB;AACjC,IAAK,KAAK,QAGV,KAAK,YAAY,KAAK,EAAI,GAErB,KAAK,QAAQ,OAChB,MAAM,KAAK,IAAI,MAAA,GAEjB,KAAK,MAAM;AAAA,EACb;AA8TF;AAhcO4G,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0ILC,aAAe9E,GAMN;AACP,aAAW,CAACS,GAAWC,CAAS,KAAKV,EAAO,SAAS;AACnD,QAAI;AACF,MAAI,OAAQU,EAAkB,SAAU,cACrCA,EAAkB,MAAA,EAAQ,MAAM,CAACxG,MAAiB;AACjD,gBAAQ,KAAK,yBAAyBuG,CAAS,KAAKvG,CAAG;AAAA,MACzD,CAAC;AAAA,IAEL,SAASA,GAAK;AACZ,cAAQ,KAAK,yBAAyBuG,CAAS,KAAKvG,CAAG;AAAA,IACzD;AAEF,EAAA8F,EAAO,SAAS,MAAA;AAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA+E,aAAmB7G,GAA0B;AAC3C,SAAOA,EAAS,SAAS,GAAG,IAAIA,EAAS,MAAM,GAAG,EAAE,IAAIA;AAC1D;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA8G,IAAA,SAAwB/G,GAAsBkC,GAAoB;AAChE,EAAAlC,EAAI,IAAI,GAAGkC,CAAI,YAAY,aAAa,EAAE,IAAI,KAAO;AACvD;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA8E,IAAA,SAAuBhH,GAAsBkC,GAAoB;AAC/D,EAAAlC,EAAI,IAAI,GAAGkC,CAAI,UAAU,YAAY,KAAK,eAAe,WAAW;AACtE;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA+E,IAAA,SAAiCjH,GAAsBkC,GAAoB;AACzE,EAAAlC,EAAI,IAAI,GAAGkC,CAAI,2BAA2B,OAAOC,GAAM1B,OACrDA,EAAM,OAAO,gBAAgB,wCAAwC,GAClD,KAAK,gBAAgB;AAAA,IACtC,SAAS;AAAA,IACT,OAAO;AAAA,IACP,aAAa;AAAA,IACb,MAAM;AAAA,IACN,YAAY,CAAA;AAAA,IACZ,UAAU,CAAA;AAAA,IACV,iBAAiB;AAAA,IACjB,iBAAiB;AAAA,EAAA,EAGpB;AACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASAyG,KAAA,SAAyBlH,GAAsBkC,GAAoB;AACjE,EAAAlC,EAAI;AAAA,IACF,GAAGkC,CAAI;AAAA,IACP,OAAO1B,GAAqBC,MAAwB;AAElD,YAAM7E,IAAU4D,EAAA,MAAKoH,GAAAS,GAAL,WAA2B7G,IAGrC4B,IAAW,CAACxG,EAAQ,SAAS,WAAW,OAAO;AAGrD,UAAImG,IAASK,IAAW,KAAK,YAAY,IAAIxG,EAAQ,QAAQ,IAAI;AACjE,UAAI,CAACmG;AACH,YAAI;AACF,gBAAMQ,IAAU,MAAM,KAAK,4BAA4B3G,CAAO;AAG9D,UAAI2G,EAAQ,eAAe,SAAS,KAClC,QAAQ;AAAA,YACN,UAAU3G,EAAQ,QAAQ,QAAQ2G,EAAQ,eAAe,MAAM,8BACzDA,EAAQ,eAAe,KAAK,IAAI,CAAC,6BACXA,EAAQ,gBAAgB,KAAK,IAAI,CAAC;AAAA,UAAA;AAIlE,gBAAM+E,IAAoB/E,EAAsE;AAChG,UAAAR,IAAS;AAAA,YACP,QAAQQ,EAAQ;AAAA,YAChB,cAAcA,EAAQ;AAAA,YACtB,iBAAiBA,EAAQ;AAAA,YACzB,gBAAgBA,EAAQ;AAAA,YACxB,UACE+E,aAA4B,MAAMA,wBAAuB,IAAA;AAAA,UAAI,GAE7DlF,KAAU,KAAK,YAAY,IAAIxG,EAAQ,UAAUmG,CAAM;AAAA,QAC7D,SAASzG,GAAO;AAEd,yBAAQ;AAAA,YACN,uDAAuDM,EAAQ,QAAQ;AAAA,YACvEN;AAAA,UAAA,GAEFmF,EAAM,KAAK,GAAG,GACPjB,EAAA,MAAKoH,GAAAW,IAAL,WAA8B;AAAA,QACvC;AAGF,YAAM/E,IAAYhC,EAAI,QAAQ,gBAAgB;AAE9C,UAAIiC;AACJ,UAAID,KAAaT,EAAO,SAAS,IAAIS,CAAS;AAC5C,QAAAC,IAAYV,EAAO,SAAS,IAAIS,CAAS;AAAA,eAChC,CAACA,KAAaE,EAAqBlC,EAAY,IAAI,GAAG;AAC/D,cAAMmC,IAAe/B,EAAA;AACrB,QAAA6B,IAAY,IAAIG,EAA8B;AAAA,UAC5C,oBAAoB,MAAMD;AAAA,UAC1B,sBAAsB,CAACE,MAAgB;AACrC,YAAAd,EAAQ,SAAS,IAAIc,GAAKJ,CAAU;AAAA,UACtC;AAAA,QAAA,CACD;AACD,YAAI;AACF,gBAAMV,EAAO,OAAO,QAAQU,CAAS;AAAA,QACvC,QAAgB;AACd,iBAAAhC,EAAM,KAAK,GAAG,GACP;AAAA,YACL,SAAS;AAAA,YACT,OAAO,EAAE,MAAM,QAAQ,SAAS,6BAAA;AAAA,YAChC,IAAI;AAAA,UAAA;AAAA,QAER;AACA,QAAAgC,EAAU,UAAU,MAAM;AACxB,UAAIA,GAAW,aACbV,EAAQ,SAAS,OAAOU,EAAU,SAAS;AAAA,QAC/C;AAAA,MACF;AACE,eAAAhC,EAAM,KAAK,GAAG,GACP;AAAA,UACL,SAAS;AAAA,UACT,OAAO,EAAE,MAAM,OAAQ,SAAS,+BAAA;AAAA,UAChC,IAAI;AAAA,QAAA;AAKR,mBAAMgC,EAAU;AAAA,QACbjC,EAAY;AAAA,QACZC,EAAc;AAAA,QACdD,EAAY;AAAA,MAAA,GAERC;AAAA,IACT;AAAA,EAAA;AAEJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA0G,KAAA,SAAwBnH,GAAsBkC,GAAoB;AAChE,EAAAlC,EAAI,IAAI,GAAGkC,CAAI,QAAQ,OAAO1B,GAAqBC,MAAwB;AACzE,UAAMC,IACJF,EAAI,QAAQ,eAAe,GAC1B,KAAA,GACGG,IACJD,KAAkBA,EAAe,SAAS,IAAIA,IAAiB;AACjE,QAAI,CAACC;AACH,aAAAF,EAAM,KAAK,GAAG,GACP;AAET,UAAMsB,IAAS,KAAK,YAAY,IAAIpB,CAAQ;AAC5C,QAAI,CAACoB;AACH,aAAAtB,EAAM,KAAK,GAAG,GACP;AAET,UAAM+B,IAAYhC,EAAI,QAAQ,gBAAgB;AAC9C,QAAI,CAACgC;AACH,aAAA/B,EAAM,KAAK,GAAG,GACP;AAET,UAAMgC,IAAYV,EAAO,SAAS,IAAIS,CAAS;AAC/C,WAAKC,KAIL,MAAMA,EAAU,cAAejC,EAAY,KAAMC,EAAc,GAAG,GAC3DA,MAJLA,EAAM,KAAK,GAAG,GACP;AAAA,EAIX,CAAC;AACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAQA2G,KAAA,SAA2BpH,GAAsBkC,GAAoB;AACnE,EAAAlC,EAAI;AAAA,IACF,GAAGkC,CAAI;AAAA,IACP,OAAO1B,GAAqBC,MAAwB;AAClD,YAAMC,IACJF,EAAI,QAAQ,eAAe,GAC1B,KAAA,GACGG,IACJD,KAAkBA,EAAe,SAAS,IAAIA,IAAiB,IAC3D8B,IAAYhC,EAAI,QAAQ,gBAAgB;AAC9C,UAAI,CAACG,KAAY,CAAC6B;AAChB,eAAA/B,EAAM,KAAK,GAAG,GACP;AAAA,UACL,SAAS;AAAA,UACT,OAAO;AAAA,YACL,MAAM;AAAA,YACN,SAAS;AAAA,UAAA;AAAA,UAEX,IAAI;AAAA,QAAA;AAGR,YAAMsB,IAAS,KAAK,YAAY,IAAIpB,CAAQ,GACtC8B,IAAYV,GAAQ,SAAS,IAAIS,CAAS;AAChD,UAAI,CAACT,KAAU,CAACU;AACd,eAAAhC,EAAM,KAAK,GAAG,GACP;AAAA,UACL,SAAS;AAAA,UACT,OAAO,EAAE,MAAM,OAAQ,SAAS,+BAAA;AAAA,UAChC,IAAI;AAAA,QAAA;AAGR,UAAI;AAEF,YAAI,OAAQgC,EAAkB,SAAU;AACtC,cAAI;AACF,kBAAOA,EAAkB,MAAA;AAAA,UAC3B,QAAQ;AAAA,UAAC;AAAA,MAEb,UAAA;AACE,QAAIA,GAAW,YAAWV,EAAO,SAAS,OAAOU,EAAU,SAAS,IAC/DV,EAAO,SAAS,OAAOS,CAAS;AAAA,MACvC;AACA,aAAA/B,EAAM,KAAK,GAAG,EAAE,KAAA,GACTA;AAAA,IACT;AAAA,EAAA;AAEJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASA4G,aAAsB7G,GAA2C;AAC/D,QAAME,IACJF,EAAI,QAAQ,eAAe,GAC1B,KAAA,GACGG,IACJD,KAAkBA,EAAe,SAAS,IACtCA,IACA,QAAQE,GAAY,IAGpBmC,IAAkC,CAAA;AACxC,aAAW,CAACvH,GAAKC,CAAK,KAAK,OAAO,QAAQ+E,EAAI,OAAO;AACnD,IAAI,OAAO/E,KAAU,aACnBsH,EAAQvH,CAAG,IAAIC;AAInB,SAAO,EAAE,UAAAkF,GAAU,SAAAoC,EAAA;AACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAUAwE,KAAA,SAAyBpL,IAAkB,iBAAiBC,IAAe,OAAQ;AACjF,SAAO;AAAA,IACL,SAAS;AAAA,IACT,OAAO;AAAA,MACL,MAAAA;AAAA,MACA,SAAAD;AAAA,IAAA;AAAA,IAEF,IAAI;AAAA,EAAA;AAER;ACvdF,SAASqL,GACPC,GAC4B;AAC5B,MAAI,CAACA,EAAQ;AAEb,QAAMrM,IAA4B;AAAA,IAChC,0BAA0BqM,EAAO;AAAA,EAAA;AAInC,SAAIA,EAAO,cAAc,UACvB,QAAQ;AAAA,IACN;AAAA,EAAA,GAIAA,EAAO,aAAa,UACtB,QAAQ;AAAA,IACN;AAAA,EAAA,GAIAA,EAAO,sBAAsB,UAC/B,QAAQ;AAAA,IACN;AAAA,EAAA,GAIAA,EAAO,oBAAoB,UAC7B,QAAQ;AAAA,IACN;AAAA,EAAA,GAKGrM;AACT;AA2DA,eAAsBsM,GACpBrN,GACA;AAEA,MAAI,CAACA,EAAQ;AACX,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAoBJ,MAdA8K,GAAyB9K,EAAQ,WAAW,GAGxCA,EAAQ,mBACV0J,EAA6B1J,EAAQ,cAAc,GAGnD,QAAQ;AAAA,IACN;AAAA,EAAA,IAMCA,EAAgB;AACnB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAMJ,MAAI,OAAOA,EAAQ,gBAAiB;AAClC,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAKJ,QAAMsN,IAAkBH;AAAA,IACtBnN,EAAQ;AAAA,EAAA,GAIJkM,IAAqB,IAAIb,GAAmBrL,EAAQ,WAAW,GAG/DqK,IAAwBrK,EAAQ,aAAA,GAIhCuN,IAAmB,IAAIjJ,EAAmB;AAAA,IAC9C,QAAQ+F;AAAA,IACR,SAASrK,EAAQ;AAAA,IACjB,eAAeA,EAAQ;AAAA,IACvB,gBAAgBsN;AAAA,IAChB,SAAStN,EAAQ;AAAA,IACjB,wBAAwB;AAAA;AAAA,IACxB,SAAS,EAAE,MAAM,UAAU,UAAU,CAAA,EAAC;AAAA,IACtC,mBAAmB;AAAA,EAAA,CACpB,GAGKqH,IAAe2E;AAAA,IACnB,CAACwB,MAA8B;AAI7B,YAAMC,IAA0BzN,EAAQ,aAAA,GAClC0N,IAAqB,IAAIpJ,EAAmB;AAAA,QAChD,QAAQmJ;AAAA,QACR,SAASzN,EAAQ;AAAA,QACjB,eAAeA,EAAQ;AAAA,QACvB,gBAAgBsN;AAAA,QAChB,SAAStN,EAAQ;AAAA,QACjB,wBAAwB;AAAA;AAAA,QACxB,SAAS,EAAE,MAAM,UAAU,UAAU,CAAA,EAAC;AAAA;AAAA,QACtC,mBAAmB;AAAA;AAAA,MAAA,CACpB;AACD,aAAO,EAAE,QAAQyN,GAAc,cAAcC,EAAA;AAAA,IAC/C;AAAA,IACAxB;AAAA,EAAA,GAII9D,IAAY,IAAIkE;AAAA,IACpBiB,EAAiB,WAAA;AAAA,IACjBlG;AAAA,IACArH,EAAQ;AAAA,IACRA,EAAQ;AAAA,EAAA;AAIV,SAAO;AAAA,IACL,QAAQqK;AAAA,IACR,OAAO,YAAY;AACjB,YAAMjC,EAAU,MAAA;AAAA,IAClB;AAAA,IACA,OAAO,YAAY;AACjB,UAAI;AAEF,cAAMA,EAAU,KAAA;AAAA,MAClB,UAAA;AAEE,QAAA8D,EAAmB,WAAA;AAAA,MACrB;AAAA,IACF;AAAA,EAAA;AAEJ;ACmCO,SAASyB,GAMdC,GAC6D;AAC7D,SAAOA;AACT;AAmCO,SAASC,GAKdD,GAS8D;AAG9D,SAAOA;AACT;"}
1
+ {"version":3,"file":"index.js","sources":["../src/mode/mode.types.ts","../src/mode/ModeResolver.ts","../src/mode/ModuleResolver.ts","../src/errors/ToolingError.ts","../src/core/ToolRegistry.ts","../src/core/DynamicToolManager.ts","../src/meta/registerMetaTools.ts","../src/core/ServerOrchestrator.ts","../src/session/ClientResourceCache.ts","../src/http/http.utils.ts","../src/http/FastifyTransport.ts","../src/session/SessionContextResolver.ts","../src/session/session.utils.ts","../src/server/server.utils.ts","../src/server/createMcpServer.ts","../src/permissions/permissions.utils.ts","../src/permissions/PermissionResolver.ts","../src/permissions/PermissionAwareFastifyTransport.ts","../src/server/createPermissionBasedMcpServer.ts"],"sourcesContent":["import type { ToolSetCatalog, ModuleLoader } from \"../types/index.js\";\n\nexport interface ModeResolverKeys {\n dynamic?: string[];\n toolsets?: string[];\n}\n\nexport interface ModeResolverOptions {\n keys?: ModeResolverKeys;\n}\n\nexport const DEFAULT_KEYS: Required<ModeResolverKeys> = {\n dynamic: [\n \"dynamic-tool-discovery\",\n \"dynamicToolDiscovery\",\n \"DYNAMIC_TOOL_DISCOVERY\",\n ],\n toolsets: [\"tool-sets\", \"toolSets\", \"FMP_TOOL_SETS\"],\n};\n\nexport const RESERVED_TOOLSET_KEYS = [\"_meta\"];\n\nexport interface ModuleResolverOptions {\n catalog: ToolSetCatalog;\n moduleLoaders?: Record<string, ModuleLoader>;\n}\n","import type { Mode, ToolSetCatalog } from \"../types/index.js\";\nimport type { ModeResolverKeys, ModeResolverOptions } from \"./mode.types.js\";\nimport { DEFAULT_KEYS } from \"./mode.types.js\";\n\nexport class ToolsetValidator {\n private readonly keys: Required<ModeResolverKeys>;\n\n constructor(options: ModeResolverOptions = {}) {\n this.keys = {\n dynamic: options.keys?.dynamic ?? DEFAULT_KEYS.dynamic,\n toolsets: options.keys?.toolsets ?? DEFAULT_KEYS.toolsets,\n };\n }\n\n static builder() {\n const opts: ModeResolverOptions = {};\n const builder = {\n keys(value: ModeResolverKeys) { opts.keys = value; return builder; },\n build() { return new ToolsetValidator(opts); },\n };\n return builder;\n }\n\n public resolveMode(\n env?: Record<string, string | undefined>,\n args?: Record<string, unknown>\n ): Mode | null {\n // Check args first\n if (this.isDynamicEnabled(args)) return \"DYNAMIC\";\n\n const toolsetsFromArgs = this.getToolsetsString(args);\n if (toolsetsFromArgs) return \"STATIC\";\n\n // Check env next\n if (this.isDynamicEnabled(env)) return \"DYNAMIC\";\n\n const toolsetsFromEnv = this.getToolsetsString(env);\n if (toolsetsFromEnv) return \"STATIC\";\n\n return null; // no override\n }\n\n public parseCommaSeparatedToolSets(\n input: string,\n catalog: ToolSetCatalog\n ): string[] {\n if (!input || typeof input !== \"string\") return [];\n const raw = input\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n\n const valid = new Set(Object.keys(catalog));\n const result: string[] = [];\n for (const name of raw) {\n if (valid.has(name)) result.push(name);\n else\n console.warn(\n `Invalid toolset '${name}' ignored. Available: ${Array.from(\n valid\n ).join(\", \")}`\n );\n }\n return result;\n }\n\n public getModulesForToolSets(\n toolsets: string[],\n catalog: ToolSetCatalog\n ): string[] {\n const modules = new Set<string>();\n for (const name of toolsets) {\n const def = catalog[name];\n if (!def) continue;\n (def.modules || []).forEach((m) => modules.add(m));\n }\n return Array.from(modules);\n }\n\n public validateToolsetName(\n name: unknown,\n catalog: ToolSetCatalog\n ): { isValid: boolean; sanitized?: string; error?: string } {\n if (!name || typeof name !== \"string\") {\n return this.createInvalidNameError(name, catalog);\n }\n const sanitized = name.trim();\n if (sanitized.length === 0) {\n return this.createInvalidNameError(sanitized, catalog);\n }\n if (!catalog[sanitized]) {\n return {\n isValid: false,\n error: `Toolset '${sanitized}' not found. Available toolsets: ${Object.keys(\n catalog\n ).join(\", \")}`,\n };\n }\n return { isValid: true, sanitized };\n }\n\n /**\n * @param name - The invalid name value\n * @param catalog - The toolset catalog for listing available options\n * @returns Validation result with descriptive error message\n */\n private createInvalidNameError(\n name: unknown,\n catalog: ToolSetCatalog\n ): { isValid: false; error: string } {\n const available = Object.keys(catalog).join(\", \");\n if (!name || typeof name !== \"string\") {\n return {\n isValid: false,\n error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${available}`,\n };\n }\n return {\n isValid: false,\n error: `Empty toolset name provided. Available toolsets: ${available}`,\n };\n }\n\n /**\n * @param toolsetNames - Array of toolset names to validate\n * @param catalog - The toolset catalog to validate against\n * @returns Validation result with modules array if valid\n */\n public validateToolsetModules(\n toolsetNames: string[],\n catalog: ToolSetCatalog\n ): { isValid: boolean; modules?: string[]; error?: string } {\n try {\n // Verify all toolset names exist in catalog first\n for (const name of toolsetNames) {\n if (!catalog[name]) {\n return {\n isValid: false,\n error: `Toolset '${name}' not found in catalog`,\n };\n }\n }\n\n // Get modules - empty array is valid (toolset may have only direct tools)\n const modules = this.getModulesForToolSets(toolsetNames, catalog);\n return { isValid: true, modules };\n } catch (error) {\n return {\n isValid: false,\n error: `Error resolving modules for ${toolsetNames.join(\", \")}: ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n };\n }\n }\n\n private isDynamicEnabled(\n source?: Record<string, unknown> | Record<string, string | undefined>\n ): boolean {\n if (!source) return false;\n for (const key of this.keys.dynamic) {\n const value = (source as any)[key];\n if (value === true) return true;\n if (typeof value === \"string\") {\n const v = value.trim().toLowerCase();\n if (v === \"true\") return true;\n }\n }\n return false;\n }\n\n private getToolsetsString(\n source?: Record<string, unknown> | Record<string, string | undefined>\n ): string | undefined {\n if (!source) return undefined;\n for (const key of this.keys.toolsets) {\n const value = (source as any)[key];\n if (typeof value === \"string\" && value.trim().length > 0)\n return value as string;\n }\n return undefined;\n }\n}\n","import type {\n ToolSetCatalog,\n ToolSetDefinition,\n McpToolDefinition,\n ModuleLoader,\n} from \"../types/index.js\";\nimport type { ModuleResolverOptions } from \"./mode.types.js\";\nimport { RESERVED_TOOLSET_KEYS } from \"./mode.types.js\";\n\nexport class ModuleResolver {\n private readonly catalog: ToolSetCatalog;\n private readonly moduleLoaders: Record<string, ModuleLoader>;\n\n constructor(options: ModuleResolverOptions) {\n // Validate catalog doesn't use reserved keys\n for (const key of RESERVED_TOOLSET_KEYS) {\n if (key in options.catalog) {\n throw new Error(\n `Toolset key '${key}' is reserved for internal use and cannot be used in the catalog`\n );\n }\n }\n this.catalog = options.catalog;\n this.moduleLoaders = options.moduleLoaders ?? {};\n }\n\n static builder() {\n const opts: Partial<ModuleResolverOptions> = {};\n const builder = {\n catalog(value: ToolSetCatalog) { opts.catalog = value; return builder; },\n moduleLoaders(value: Record<string, ModuleLoader>) { opts.moduleLoaders = value; return builder; },\n build() { return new ModuleResolver(opts as ModuleResolverOptions); },\n };\n return builder;\n }\n\n public getAvailableToolsets(): string[] {\n return Object.keys(this.catalog);\n }\n\n public getToolsetDefinition(name: string): ToolSetDefinition | undefined {\n return this.catalog[name];\n }\n\n public validateToolsetName(name: unknown): {\n isValid: boolean;\n sanitized?: string;\n error?: string;\n } {\n if (!name || typeof name !== \"string\") {\n return {\n isValid: false,\n error: `Invalid toolset name provided. Must be a non-empty string. Available toolsets: ${this.getAvailableToolsets().join(\n \", \"\n )}`,\n };\n }\n const sanitized = name.trim();\n if (sanitized.length === 0) {\n return {\n isValid: false,\n error: `Empty toolset name provided. Available toolsets: ${this.getAvailableToolsets().join(\n \", \"\n )}`,\n };\n }\n // Check for reserved keys (defense in depth)\n if (RESERVED_TOOLSET_KEYS.includes(sanitized)) {\n return {\n isValid: false,\n error: `Toolset key '${sanitized}' is reserved for internal use`,\n };\n }\n if (!this.catalog[sanitized]) {\n return {\n isValid: false,\n error: `Toolset '${sanitized}' not found. Available toolsets: ${this.getAvailableToolsets().join(\n \", \"\n )}`,\n };\n }\n return { isValid: true, sanitized };\n }\n\n public async resolveToolsForToolsets(\n toolsets: string[],\n context?: unknown\n ): Promise<McpToolDefinition[]> {\n const collected: McpToolDefinition[] = [];\n for (const name of toolsets) {\n const def = this.catalog[name];\n if (!def) continue;\n this.collectDirectTools(def, collected);\n await this.loadModuleTools(def, name, context, collected);\n }\n return collected;\n }\n\n /**\n * @param def - The toolset definition\n * @param collected - Mutable array to append direct tools to\n */\n private collectDirectTools(\n def: ToolSetDefinition,\n collected: McpToolDefinition[]\n ): void {\n if (Array.isArray(def.tools) && def.tools.length > 0) {\n collected.push(...def.tools);\n }\n }\n\n /**\n * @param def - The toolset definition containing module keys\n * @param toolsetName - The toolset name for error messages\n * @param context - Optional context passed to module loaders\n * @param collected - Mutable array to append loaded tools to\n */\n private async loadModuleTools(\n def: ToolSetDefinition,\n toolsetName: string,\n context: unknown,\n collected: McpToolDefinition[]\n ): Promise<void> {\n if (!Array.isArray(def.modules) || def.modules.length === 0) return;\n for (const modKey of def.modules) {\n const loader = this.moduleLoaders[modKey];\n if (!loader) continue;\n try {\n const loaded = await loader(context);\n if (Array.isArray(loaded) && loaded.length > 0) {\n collected.push(...loaded);\n }\n } catch (err) {\n console.warn(\n `Module loader '${modKey}' failed for toolset '${toolsetName}':`,\n err\n );\n }\n }\n }\n}\n","import type { ToolingErrorCode } from \"../types/index.js\";\n\nexport class ToolingError extends Error {\n public readonly code: ToolingErrorCode;\n public readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: ToolingErrorCode,\n details?: Record<string, unknown>,\n _options?: unknown\n ) {\n super(message);\n this.name = \"ToolingError\";\n this.code = code;\n this.details = details;\n }\n}\n","import type { McpToolDefinition } from \"../types/index.js\";\nimport { ToolingError } from \"../errors/ToolingError.js\";\nimport type { ToolRegistryOptions } from \"./core.types.js\";\n\nexport class ToolRegistry {\n private readonly options: Required<ToolRegistryOptions>;\n private readonly names = new Set<string>();\n private readonly toolsetToNames = new Map<string, Set<string>>();\n\n constructor(options: ToolRegistryOptions = {}) {\n this.options = {\n namespaceWithToolset: options.namespaceWithToolset ?? true,\n };\n }\n\n static builder() {\n const opts: ToolRegistryOptions = {};\n const builder = {\n namespaceWithToolset(value: boolean) { opts.namespaceWithToolset = value; return builder; },\n build() { return new ToolRegistry(opts); },\n };\n return builder;\n }\n\n public getSafeName(toolsetKey: string, toolName: string): string {\n if (!this.options.namespaceWithToolset) return toolName;\n if (toolName.startsWith(`${toolsetKey}.`)) return toolName;\n return `${toolsetKey}.${toolName}`;\n }\n\n public has(name: string): boolean {\n return this.names.has(name);\n }\n\n public add(name: string): void {\n if (this.names.has(name)) {\n throw new ToolingError(\n `Tool name collision: '${name}' already registered`,\n \"E_TOOL_NAME_CONFLICT\"\n );\n }\n this.names.add(name);\n }\n\n public addForToolset(toolsetKey: string, name: string): void {\n this.add(name);\n const set = this.toolsetToNames.get(toolsetKey) ?? new Set<string>();\n set.add(name);\n this.toolsetToNames.set(toolsetKey, set);\n }\n\n public mapAndValidate(\n toolsetKey: string,\n tools: McpToolDefinition[]\n ): McpToolDefinition[] {\n return tools.map((t) => {\n const safe = this.getSafeName(toolsetKey, t.name);\n if (this.has(safe)) {\n throw new ToolingError(\n `Tool name collision for '${safe}'`,\n \"E_TOOL_NAME_CONFLICT\"\n );\n }\n return { ...t, name: safe };\n });\n }\n\n public list(): string[] {\n return Array.from(this.names);\n }\n\n public listByToolset(): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n for (const [k, v] of this.toolsetToNames.entries()) {\n result[k] = Array.from(v);\n }\n return result;\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type {\n ExposurePolicy,\n McpToolDefinition,\n ToolSetDefinition,\n ToolingErrorCode,\n} from \"../types/index.js\";\nimport { ModuleResolver } from \"../mode/ModuleResolver.js\";\nimport { ToolRegistry } from \"./ToolRegistry.js\";\nimport type { DynamicToolManagerOptions } from \"./core.types.js\";\n\nexport class DynamicToolManager {\n private readonly server: McpServer;\n private readonly resolver: ModuleResolver;\n private readonly context?: unknown;\n private readonly onToolsListChanged?: () => Promise<void> | void;\n private readonly exposurePolicy?: ExposurePolicy;\n private readonly toolRegistry: ToolRegistry;\n\n private readonly activeToolsets = new Set<string>();\n\n constructor(options: DynamicToolManagerOptions) {\n this.server = options.server;\n this.resolver = options.resolver;\n this.context = options.context;\n this.onToolsListChanged = options.onToolsListChanged;\n this.exposurePolicy = options.exposurePolicy;\n this.toolRegistry =\n options.toolRegistry ?? ToolRegistry.builder().namespaceWithToolset(true).build();\n }\n\n static builder() {\n const opts: Partial<DynamicToolManagerOptions> = {};\n const builder = {\n server(value: McpServer) { opts.server = value; return builder; },\n resolver(value: ModuleResolver) { opts.resolver = value; return builder; },\n context(value: unknown) { opts.context = value; return builder; },\n onToolsListChanged(value: () => Promise<void> | void) { opts.onToolsListChanged = value; return builder; },\n exposurePolicy(value: ExposurePolicy) { opts.exposurePolicy = value; return builder; },\n toolRegistry(value: ToolRegistry) { opts.toolRegistry = value; return builder; },\n build() { return new DynamicToolManager(opts as DynamicToolManagerOptions); },\n };\n return builder;\n }\n\n /**\n * @returns Promise that resolves when notification is sent (or skipped)\n */\n private async notifyToolsChanged(): Promise<void> {\n if (!this.onToolsListChanged) return;\n try {\n await this.onToolsListChanged();\n } catch (err) {\n console.warn(\"Failed to send tool list change notification:\", err);\n }\n }\n\n public getAvailableToolsets(): string[] {\n return this.resolver.getAvailableToolsets();\n }\n\n public getActiveToolsets(): string[] {\n return Array.from(this.activeToolsets);\n }\n\n public getToolsetDefinition(name: string): ToolSetDefinition | undefined {\n return this.resolver.getToolsetDefinition(name);\n }\n\n public isActive(name: string): boolean {\n return this.activeToolsets.has(name);\n }\n\n /**\n * Enables a single toolset by name.\n * @param toolsetName - The name of the toolset to enable\n * @param skipNotification - If true, skips the tool list change notification\n * @returns Result object with success status and message\n */\n public async enableToolset(\n toolsetName: string,\n skipNotification = false\n ): Promise<{ success: boolean; message: string }> {\n const validation = this.validateToolsetForEnable(toolsetName);\n if (\"message\" in validation) return validation;\n\n const { sanitized } = validation;\n\n // Check exposure policies BEFORE resolving tools to fail fast\n const policyCheck = this.checkExposurePolicy(sanitized);\n if (!policyCheck.allowed) {\n return { success: false, message: policyCheck.message };\n }\n\n // Track tools registered for this enable operation to allow rollback\n const registeredTools: string[] = [];\n\n try {\n const toolCount = await this.resolveAndRegisterTools(sanitized, registeredTools);\n\n // Track state only after successful registration\n this.activeToolsets.add(sanitized);\n\n // Notify list change (unless skipped for batch operations)\n if (!skipNotification) {\n await this.notifyToolsChanged();\n }\n\n return this.buildEnableResult(sanitized, toolCount);\n } catch (error) {\n this.handlePartialFailure(sanitized, registeredTools);\n return {\n success: false,\n message: `Failed to enable toolset '${sanitized}': ${\n error instanceof Error ? error.message : \"Unknown error\"\n }`,\n };\n }\n }\n\n /**\n * @param toolsetName - The raw toolset name to validate\n * @returns Error result if invalid, or `{ sanitized }` to continue\n */\n private validateToolsetForEnable(\n toolsetName: string\n ): { success: boolean; message: string } | { sanitized: string } {\n const validation = this.resolver.validateToolsetName(toolsetName);\n if (!validation.isValid || !validation.sanitized) {\n return {\n success: false,\n message: validation.error || \"Unknown validation error\",\n };\n }\n if (this.activeToolsets.has(validation.sanitized)) {\n return {\n success: false,\n message: `Toolset '${validation.sanitized}' is already enabled.`,\n };\n }\n return { sanitized: validation.sanitized };\n }\n\n /**\n * @param sanitized - The validated toolset name\n * @param registeredTools - Mutable array tracking registered tool names for rollback\n * @returns The number of tools resolved\n */\n private async resolveAndRegisterTools(\n sanitized: string,\n registeredTools: string[]\n ): Promise<number> {\n const resolvedTools = await this.resolver.resolveToolsForToolsets(\n [sanitized],\n this.context\n );\n\n if (resolvedTools && resolvedTools.length > 0) {\n const mapped = this.toolRegistry.mapAndValidate(sanitized, resolvedTools);\n for (const tool of mapped) {\n this.registerSingleTool(tool, sanitized);\n registeredTools.push(tool.name);\n }\n }\n\n return resolvedTools?.length ?? 0;\n }\n\n /**\n * @param sanitized - The toolset name\n * @param toolCount - Number of tools registered\n * @returns Success result object\n */\n private buildEnableResult(\n sanitized: string,\n toolCount: number\n ): { success: boolean; message: string } {\n return {\n success: true,\n message: `Toolset '${sanitized}' enabled successfully. Registered ${toolCount} tools.`,\n };\n }\n\n /**\n * @param sanitized - The toolset name that partially failed\n * @param registeredTools - Tools that were registered before the failure\n */\n private handlePartialFailure(\n sanitized: string,\n registeredTools: string[]\n ): void {\n if (registeredTools.length > 0) {\n console.warn(\n `Partial failure enabling toolset '${sanitized}'. ` +\n `${registeredTools.length} tools were registered but toolset activation failed. ` +\n `Tools remain registered due to MCP limitations: ${registeredTools.join(\", \")}`\n );\n }\n }\n\n /**\n * @param toolsetName - The sanitized toolset name to check\n * @returns Object indicating if allowed and reason message if not\n */\n private checkExposurePolicy(toolsetName: string): {\n allowed: boolean;\n message: string;\n } {\n if (\n this.exposurePolicy?.allowlist &&\n !this.exposurePolicy.allowlist.includes(toolsetName)\n ) {\n return {\n allowed: false,\n message: `Toolset '${toolsetName}' is not allowed by policy.`,\n };\n }\n if (\n this.exposurePolicy?.denylist &&\n this.exposurePolicy.denylist.includes(toolsetName)\n ) {\n return {\n allowed: false,\n message: `Toolset '${toolsetName}' is denied by policy.`,\n };\n }\n if (this.exposurePolicy?.maxActiveToolsets !== undefined) {\n const next = this.activeToolsets.size + 1;\n if (next > this.exposurePolicy.maxActiveToolsets) {\n this.exposurePolicy.onLimitExceeded?.(\n [toolsetName],\n Array.from(this.activeToolsets)\n );\n return {\n allowed: false,\n message: `Activation exceeds maxActiveToolsets (${this.exposurePolicy.maxActiveToolsets}).`,\n };\n }\n }\n return { allowed: true, message: \"\" };\n }\n\n /**\n * @param tool - The tool definition to register\n * @param toolsetKey - The toolset key for tracking\n */\n private registerSingleTool(tool: McpToolDefinition, toolsetKey: string): void {\n // Only pass annotations if they exist and are not empty\n const hasAnnotations =\n tool.annotations && Object.keys(tool.annotations).length > 0;\n\n if (hasAnnotations && tool.annotations) {\n this.server.tool(\n tool.name,\n tool.description,\n tool.inputSchema as Parameters<typeof this.server.tool>[2],\n tool.annotations,\n async (args: Record<string, unknown>) => {\n return await tool.handler(args);\n }\n );\n } else {\n // Legacy 4-parameter call when no annotations\n this.server.tool(\n tool.name,\n tool.description,\n tool.inputSchema as Parameters<typeof this.server.tool>[2],\n async (args: Record<string, unknown>) => {\n return await tool.handler(args);\n }\n );\n }\n this.toolRegistry.addForToolset(toolsetKey, tool.name);\n }\n\n /**\n * @param toolsetName - The name of the toolset to disable\n * @returns Result object with success status and message\n */\n public async disableToolset(\n toolsetName: string\n ): Promise<{ success: boolean; message: string }> {\n const validation = this.resolver.validateToolsetName(toolsetName);\n if (!validation.isValid || !validation.sanitized) {\n const activeToolsets =\n Array.from(this.activeToolsets).join(\", \") || \"none\";\n const base = validation.error || \"Unknown validation error\";\n return {\n success: false,\n message: `${base} Active toolsets: ${activeToolsets}`,\n };\n }\n const sanitized = validation.sanitized;\n if (!this.activeToolsets.has(sanitized)) {\n return {\n success: false,\n message: `Toolset '${sanitized}' is not currently active. Active toolsets: ${\n Array.from(this.activeToolsets).join(\", \") || \"none\"\n }`,\n };\n }\n\n // State-only disable; no unregistration support in MCP\n this.activeToolsets.delete(sanitized);\n\n await this.notifyToolsChanged();\n\n return {\n success: true,\n message: `Toolset '${sanitized}' disabled successfully. Individual tools remain registered due to MCP limitations.`,\n };\n }\n\n public getStatus() {\n return {\n availableToolsets: this.getAvailableToolsets(),\n activeToolsets: this.getActiveToolsets(),\n registeredModules: [],\n totalToolsets: this.getAvailableToolsets().length,\n activeCount: this.activeToolsets.size,\n tools: this.toolRegistry.list(),\n toolsetToTools: this.toolRegistry.listByToolset(),\n };\n }\n\n /**\n * @param toolsetNames - Array of toolset names to enable\n * @returns Result object with overall success status and individual results\n */\n public async enableToolsets(toolsetNames: string[]): Promise<{\n success: boolean;\n results: Array<{\n name: string;\n success: boolean;\n message: string;\n code?: ToolingErrorCode;\n }>;\n message: string;\n }> {\n const results: Array<{\n name: string;\n success: boolean;\n message: string;\n code?: ToolingErrorCode;\n }> = [];\n\n // Enable each toolset, skipping individual notifications\n for (const name of toolsetNames) {\n try {\n const res = await this.enableToolset(name, true);\n results.push({ name, ...res });\n } catch (err) {\n results.push({\n name,\n success: false,\n message: err instanceof Error ? err.message : \"Unknown error\",\n code: \"E_INTERNAL\",\n });\n }\n }\n\n const successAll = results.every((r) => r.success);\n const anySuccess = results.some((r) => r.success);\n const message = successAll\n ? \"All toolsets enabled\"\n : anySuccess\n ? \"Some toolsets failed to enable\"\n : \"All toolsets failed to enable\";\n\n // Send a single notification after batch is complete (if any changes occurred)\n if (anySuccess) {\n await this.notifyToolsChanged();\n }\n\n return { success: successAll, results, message };\n }\n\n /**\n * @returns Result object with overall success status and individual results\n */\n public async enableAllToolsets(): Promise<{\n success: boolean;\n results: Array<{\n name: string;\n success: boolean;\n message: string;\n code?: ToolingErrorCode;\n }>;\n message: string;\n }> {\n const all = this.getAvailableToolsets();\n return this.enableToolsets(all);\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Mode } from \"../types/index.js\";\nimport { z } from \"zod\";\nimport { DynamicToolManager } from \"../core/DynamicToolManager.js\";\nimport { ToolRegistry } from \"../core/ToolRegistry.js\";\n\n/**\n * Reserved toolset key for meta-tools.\n * Meta-tools are registered under this key to enable collision detection\n * and tracking via the ToolRegistry.\n */\nexport const META_TOOLSET_KEY = \"_meta\";\n\n/**\n * Registers meta-tools on the MCP server for toolset management.\n *\n * In DYNAMIC mode, all meta-tools are registered:\n * - enable_toolset, disable_toolset: For runtime toolset management\n * - list_toolsets, describe_toolset: For toolset discovery\n * - list_tools: For listing registered tools\n *\n * In STATIC mode, only list_tools is registered since toolsets are fixed at startup.\n *\n * Meta-tools are registered with the ToolRegistry under the reserved \"_meta\" toolset key\n * to enable collision detection with user-defined tools.\n *\n * @param server - The MCP server to register tools on\n * @param manager - The DynamicToolManager instance\n * @param toolRegistry - The ToolRegistry for collision detection\n * @param options - Configuration options including the mode\n */\nexport function registerMetaTools(\n server: McpServer,\n manager: DynamicToolManager,\n toolRegistry: ToolRegistry,\n options?: { mode?: Exclude<Mode, \"ALL\"> }\n): void {\n const mode = options?.mode ?? \"DYNAMIC\";\n\n // Dynamic-mode only tools: enable/disable toolsets at runtime\n if (mode === \"DYNAMIC\") {\n // Register with ToolRegistry for collision detection before server.tool()\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"enable_toolset\");\n server.tool(\n \"enable_toolset\",\n \"Enable a toolset by name\",\n { name: z.string().describe(\"Toolset name\") },\n { destructiveHint: true, idempotentHint: true },\n async (args: { name: string }) => {\n const result = await manager.enableToolset(args.name);\n return {\n content: [{ type: \"text\", text: JSON.stringify(result) }],\n };\n }\n );\n\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"disable_toolset\");\n server.tool(\n \"disable_toolset\",\n \"Disable a toolset by name (state only)\",\n { name: z.string().describe(\"Toolset name\") },\n { destructiveHint: true, idempotentHint: true },\n async (args: { name: string }) => {\n const result = await manager.disableToolset(args.name);\n return {\n content: [{ type: \"text\", text: JSON.stringify(result) }],\n };\n }\n );\n\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"list_toolsets\");\n server.tool(\n \"list_toolsets\",\n \"List available toolsets with active status and definitions\",\n {},\n { readOnlyHint: true, idempotentHint: true },\n async () => {\n const available = manager.getAvailableToolsets();\n const byToolset = manager.getStatus().toolsetToTools;\n const items = available.map((key) => {\n const def = manager.getToolsetDefinition(key);\n return {\n key,\n active: manager.isActive(key),\n definition: def\n ? {\n name: def.name,\n description: def.description,\n modules: def.modules ?? [],\n decisionCriteria: def.decisionCriteria ?? undefined,\n }\n : null,\n tools: byToolset[key] ?? [],\n };\n });\n return {\n content: [\n { type: \"text\", text: JSON.stringify({ toolsets: items }) },\n ],\n };\n }\n );\n\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"describe_toolset\");\n server.tool(\n \"describe_toolset\",\n \"Describe a toolset with definition, active status and tools\",\n { name: z.string().describe(\"Toolset name\") },\n { readOnlyHint: true, idempotentHint: true },\n async (args: { name: string }) => {\n const def = manager.getToolsetDefinition(args.name);\n const byToolset = manager.getStatus().toolsetToTools;\n if (!def) {\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify({ error: `Unknown toolset '${args.name}'` }),\n },\n ],\n };\n }\n const payload = {\n key: args.name,\n active: manager.isActive(args.name),\n definition: {\n name: def.name,\n description: def.description,\n modules: def.modules ?? [],\n decisionCriteria: def.decisionCriteria ?? undefined,\n },\n tools: byToolset[args.name] ?? [],\n };\n return {\n content: [{ type: \"text\", text: JSON.stringify(payload) }],\n };\n }\n );\n }\n\n // list_tools is available in both modes\n toolRegistry.addForToolset(META_TOOLSET_KEY, \"list_tools\");\n server.tool(\n \"list_tools\",\n \"List currently registered tool names (best effort)\",\n {},\n { readOnlyHint: true, idempotentHint: true },\n async () => {\n const status = manager.getStatus();\n const payload = {\n tools: status.tools,\n toolsetToTools: status.toolsetToTools,\n };\n return {\n content: [{ type: \"text\", text: JSON.stringify(payload) }],\n };\n }\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { ToolsetValidator } from \"../mode/ToolsetValidator.js\";\nimport { ModuleResolver } from \"../mode/ModuleResolver.js\";\nimport { DynamicToolManager } from \"./DynamicToolManager.js\";\nimport { registerMetaTools } from \"../meta/registerMetaTools.js\";\nimport type {\n ExposurePolicy,\n Mode,\n ModuleLoader,\n ToolSetCatalog,\n} from \"../types/index.js\";\nimport { ToolRegistry } from \"./ToolRegistry.js\";\nimport type { ServerOrchestratorOptions } from \"./core.types.js\";\n\nexport class ServerOrchestrator {\n private readonly mode: Exclude<Mode, \"ALL\">;\n private readonly resolver: ModuleResolver;\n private readonly manager: DynamicToolManager;\n private readonly toolsetValidator: ToolsetValidator;\n private readonly initPromise: Promise<void>;\n private initError: Error | null = null;\n\n constructor(options: ServerOrchestratorOptions) {\n this.toolsetValidator = ToolsetValidator.builder().build();\n const startup = options.startup ?? {};\n const resolved = this.resolveStartupConfig(startup, options.catalog);\n this.mode = resolved.mode;\n this.resolver = ModuleResolver.builder()\n .catalog(options.catalog)\n .moduleLoaders(options.moduleLoaders ?? {})\n .build();\n const toolRegistry = ToolRegistry.builder()\n .namespaceWithToolset(\n options.exposurePolicy?.namespaceToolsWithSetKey ?? true\n )\n .build();\n const managerBuilder = DynamicToolManager.builder()\n .server(options.server)\n .resolver(this.resolver)\n .context(options.context)\n .toolRegistry(toolRegistry);\n\n if (options.notifyToolsListChanged) {\n managerBuilder.onToolsListChanged(options.notifyToolsListChanged);\n }\n if (options.exposurePolicy) {\n managerBuilder.exposurePolicy(options.exposurePolicy);\n }\n\n this.manager = managerBuilder.build();\n\n // Register meta-tools only if requested (default true)\n if (options.registerMetaTools !== false) {\n registerMetaTools(options.server, this.manager, toolRegistry, { mode: this.mode });\n }\n\n // Startup behavior - store promise for async initialization\n const initial = resolved.toolsets;\n this.initPromise = this.initializeToolsets(initial);\n }\n\n static builder() {\n const opts: Partial<ServerOrchestratorOptions> = {};\n const builder = {\n server(value: McpServer) { opts.server = value; return builder; },\n catalog(value: ToolSetCatalog) { opts.catalog = value; return builder; },\n moduleLoaders(value: Record<string, ModuleLoader>) { opts.moduleLoaders = value; return builder; },\n exposurePolicy(value: ExposurePolicy) { opts.exposurePolicy = value; return builder; },\n context(value: unknown) { opts.context = value; return builder; },\n notifyToolsListChanged(value: () => Promise<void> | void) { opts.notifyToolsListChanged = value; return builder; },\n startup(value: { mode?: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" }) { opts.startup = value; return builder; },\n registerMetaTools(value: boolean) { opts.registerMetaTools = value; return builder; },\n build() { return new ServerOrchestrator(opts as ServerOrchestratorOptions); },\n };\n return builder;\n }\n\n /**\n * @param initial - The toolsets to initialize or \"ALL\"\n * @returns Promise that resolves when initialization is complete\n */\n private async initializeToolsets(\n initial: string[] | \"ALL\" | undefined\n ): Promise<void> {\n try {\n if (initial === \"ALL\") {\n await this.manager.enableToolsets(this.resolver.getAvailableToolsets());\n } else if (Array.isArray(initial) && initial.length > 0) {\n await this.manager.enableToolsets(initial);\n }\n } catch (error) {\n this.initError =\n error instanceof Error ? error : new Error(String(error));\n console.error(\"Failed to initialize toolsets:\", this.initError);\n }\n }\n\n public async ensureReady(): Promise<void> {\n await this.initPromise;\n if (this.initError) {\n throw this.initError;\n }\n }\n\n public async isReady(): Promise<boolean> {\n await this.initPromise;\n return this.initError === null;\n }\n\n private resolveStartupConfig(\n startup: { mode?: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" },\n catalog: ToolSetCatalog\n ): { mode: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" } {\n if (startup.mode) {\n return this.resolveExplicitMode(startup.mode, startup.toolsets, catalog);\n }\n return this.inferModeFromToolsets(startup, catalog);\n }\n\n /**\n * @param mode - The explicit mode\n * @param toolsets - Optional toolsets from startup config\n * @param catalog - The toolset catalog to validate against\n * @returns Resolved mode and toolsets\n */\n private resolveExplicitMode(\n mode: Exclude<Mode, \"ALL\">,\n toolsets: string[] | \"ALL\" | undefined,\n catalog: ToolSetCatalog\n ): { mode: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" } {\n if (mode === \"DYNAMIC\" && toolsets) {\n console.warn(\"startup.toolsets provided but ignored in DYNAMIC mode\");\n return { mode: \"DYNAMIC\" };\n }\n if (mode === \"STATIC\") {\n if (toolsets === \"ALL\")\n return { mode: \"STATIC\", toolsets: \"ALL\" };\n const names = Array.isArray(toolsets) ? toolsets : [];\n const valid = this.validateAndCollectToolsets(names, catalog);\n if (names.length > 0 && valid.length === 0) {\n throw new Error(\n \"STATIC mode requires valid toolsets or 'ALL'; none were valid\"\n );\n }\n return { mode: \"STATIC\", toolsets: valid };\n }\n return { mode };\n }\n\n /**\n * @param startup - Startup config without an explicit mode\n * @param catalog - The toolset catalog to validate against\n * @returns Inferred mode and toolsets\n */\n private inferModeFromToolsets(\n startup: { toolsets?: string[] | \"ALL\" },\n catalog: ToolSetCatalog\n ): { mode: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" } {\n if (startup.toolsets === \"ALL\") return { mode: \"STATIC\", toolsets: \"ALL\" };\n if (Array.isArray(startup.toolsets) && startup.toolsets.length > 0) {\n const valid = this.validateAndCollectToolsets(startup.toolsets, catalog);\n if (valid.length === 0) {\n throw new Error(\n \"STATIC mode requires valid toolsets or 'ALL'; none were valid\"\n );\n }\n return { mode: \"STATIC\", toolsets: valid };\n }\n return { mode: \"DYNAMIC\" };\n }\n\n /**\n * @param names - Array of toolset names to validate\n * @param catalog - The toolset catalog to validate against\n * @returns Array of valid, sanitized toolset names\n */\n private validateAndCollectToolsets(\n names: string[],\n catalog: ToolSetCatalog\n ): string[] {\n const valid: string[] = [];\n for (const name of names) {\n const { isValid, sanitized, error } =\n this.toolsetValidator.validateToolsetName(name, catalog);\n if (isValid && sanitized) valid.push(sanitized);\n else if (error) console.warn(error);\n }\n return valid;\n }\n\n public getMode(): Exclude<Mode, \"ALL\"> {\n return this.mode;\n }\n\n public getManager(): DynamicToolManager {\n return this.manager;\n }\n}\n","import type { ClientResourceCacheOptions, Entry } from \"./session.types.js\";\n\nexport class ClientResourceCache<T> {\n private storage = new Map<string, Entry<T>>();\n private maxSize: number;\n private ttlMs: number;\n private onEvict?: (key: string, resource: T) => void | Promise<void>;\n // Use ReturnType<typeof setInterval> for cross-env typings without NodeJS namespace\n private pruneInterval?: ReturnType<typeof setInterval>;\n\n constructor(options: ClientResourceCacheOptions<T> = {}) {\n this.maxSize = options.maxSize ?? 1000;\n this.ttlMs = options.ttlMs ?? 1000 * 60 * 60;\n this.onEvict = options.onEvict;\n const pruneEvery = options.pruneIntervalMs ?? 1000 * 60 * 10;\n this.pruneInterval = setInterval(() => this.pruneExpired(), pruneEvery);\n }\n\n static builder<T>() {\n const opts: ClientResourceCacheOptions<T> = {};\n const builder = {\n maxSize(value: number) { opts.maxSize = value; return builder; },\n ttlMs(value: number) { opts.ttlMs = value; return builder; },\n pruneIntervalMs(value: number) { opts.pruneIntervalMs = value; return builder; },\n onEvict(value: (key: string, resource: T) => void | Promise<void>) { opts.onEvict = value; return builder; },\n build() { return new ClientResourceCache<T>(opts); },\n };\n return builder;\n }\n\n public getEntryCount(): number {\n return this.storage.size;\n }\n\n public getMaxSize(): number {\n return this.maxSize;\n }\n\n public getTtl(): number {\n return this.ttlMs;\n }\n\n public get(key: string): T | null {\n const entry = this.storage.get(key);\n if (!entry) return null;\n if (Date.now() - entry.lastAccessed > this.ttlMs) {\n this.delete(key);\n return null;\n }\n entry.lastAccessed = Date.now();\n this.storage.delete(key);\n this.storage.set(key, entry);\n return entry.resource;\n }\n\n public set(key: string, resource: T): void {\n if (this.storage.size >= this.maxSize) {\n this.evictLeastRecentlyUsed();\n }\n const newEntry: Entry<T> = { resource, lastAccessed: Date.now() };\n this.storage.set(key, newEntry);\n }\n\n /**\n * @param key - The key to remove\n */\n public delete(key: string): void {\n const entry = this.storage.get(key);\n if (entry) {\n this.storage.delete(key);\n this.#callEvictCallback(key, entry.resource);\n }\n }\n\n /**\n * @param clearEntries - If true, also removes all entries and calls onEvict for each\n */\n public stop(clearEntries = false): void {\n if (this.pruneInterval) {\n clearInterval(this.pruneInterval);\n this.pruneInterval = undefined;\n }\n if (clearEntries) {\n this.clear();\n }\n }\n\n public clear(): void {\n // Collect all entries first to avoid modification during iteration\n const entries = Array.from(this.storage.entries());\n this.storage.clear();\n for (const [key, entry] of entries) {\n this.#callEvictCallback(key, entry.resource);\n }\n }\n\n private evictLeastRecentlyUsed(): void {\n const lruKey = this.storage.keys().next().value as string | undefined;\n if (lruKey) {\n this.delete(lruKey);\n }\n }\n\n private pruneExpired(): void {\n const now = Date.now();\n const keysToDelete: string[] = [];\n for (const [key, entry] of this.storage.entries()) {\n if (now - entry.lastAccessed > this.ttlMs) {\n keysToDelete.push(key);\n }\n }\n // Delete after iteration to avoid modification during iteration\n for (const key of keysToDelete) {\n this.delete(key);\n }\n }\n\n /**\n * @param key - The key being evicted\n * @param resource - The resource being evicted\n */\n #callEvictCallback(key: string, resource: T): void {\n if (!this.onEvict) return;\n try {\n const result = this.onEvict(key, resource);\n // Handle async callbacks but don't await\n if (result instanceof Promise) {\n result.catch((err) => {\n console.warn(`Error in cache eviction callback for key '${key}':`, err);\n });\n }\n } catch (err) {\n console.warn(`Error in cache eviction callback for key '${key}':`, err);\n }\n }\n}\n","import type { FastifyInstance, FastifyRequest, FastifyReply } from \"fastify\";\nimport { z } from \"zod\";\nimport { randomUUID } from \"node:crypto\";\nimport type {\n CustomEndpointDefinition,\n CustomEndpointRequest,\n EndpointErrorResponse,\n HttpMethod,\n PermissionAwareEndpointHandler,\n PermissionAwareEndpointRequest,\n RegisterCustomEndpointsOptions,\n} from \"./http.types.js\";\n\n// --- defineEndpoint (from customEndpoints.ts) ---\n\n/**\n * Helper function to create type-safe custom endpoints with automatic type inference.\n * Provides better IntelliSense and type checking for endpoint definitions.\n *\n * @template TBody - Zod schema for request body\n * @template TQuery - Zod schema for query parameters\n * @template TParams - Zod schema for path parameters\n * @template TResponse - Zod schema for response\n *\n * @param definition - Endpoint definition with schemas and handler\n * @returns The same endpoint definition with full type inference\n *\n * @example\n * ```typescript\n * import { z } from \"zod\";\n * import { defineEndpoint } from \"toolception\";\n *\n * const getUsersEndpoint = defineEndpoint({\n * method: \"GET\",\n * path: \"/users\",\n * querySchema: z.object({\n * limit: z.coerce.number().int().positive().default(10),\n * role: z.enum([\"admin\", \"user\"]).optional(),\n * }),\n * responseSchema: z.object({\n * users: z.array(z.object({\n * id: z.string(),\n * name: z.string(),\n * })),\n * total: z.number(),\n * }),\n * handler: async (req) => {\n * // req.query is fully typed: { limit: number, role?: \"admin\" | \"user\" }\n * const { limit, role } = req.query;\n *\n * return {\n * users: [{ id: \"1\", name: \"Alice\" }],\n * total: 1,\n * };\n * },\n * });\n * ```\n */\nexport function defineEndpoint<\n TBody extends z.ZodTypeAny = z.ZodNever,\n TQuery extends z.ZodTypeAny = z.ZodNever,\n TParams extends z.ZodTypeAny = z.ZodNever,\n TResponse extends z.ZodTypeAny = z.ZodAny\n>(\n definition: CustomEndpointDefinition<TBody, TQuery, TParams, TResponse>\n): CustomEndpointDefinition<TBody, TQuery, TParams, TResponse> {\n return definition;\n}\n\n// --- definePermissionAwareEndpoint (from customEndpoints.ts) ---\n\n/**\n * Helper function to create permission-aware custom endpoints for permission-based servers.\n * Similar to defineEndpoint but with access to permission context in the handler.\n *\n * @template TBody - Zod schema for request body\n * @template TQuery - Zod schema for query parameters\n * @template TParams - Zod schema for path parameters\n * @template TResponse - Zod schema for response\n *\n * @param definition - Endpoint definition with permission-aware handler\n * @returns Endpoint definition compatible with permission-based servers\n *\n * @example\n * ```typescript\n * import { definePermissionAwareEndpoint } from \"toolception\";\n *\n * const statsEndpoint = definePermissionAwareEndpoint({\n * method: \"GET\",\n * path: \"/my-permissions\",\n * responseSchema: z.object({\n * toolsets: z.array(z.string()),\n * count: z.number(),\n * }),\n * handler: async (req) => {\n * // req.allowedToolsets and req.failedToolsets are available\n * return {\n * toolsets: req.allowedToolsets,\n * count: req.allowedToolsets.length,\n * };\n * },\n * });\n * ```\n */\nexport function definePermissionAwareEndpoint<\n TBody extends z.ZodTypeAny = z.ZodNever,\n TQuery extends z.ZodTypeAny = z.ZodNever,\n TParams extends z.ZodTypeAny = z.ZodNever,\n TResponse extends z.ZodTypeAny = z.ZodAny\n>(definition: {\n method: HttpMethod;\n path: string;\n bodySchema?: TBody;\n querySchema?: TQuery;\n paramsSchema?: TParams;\n responseSchema?: TResponse;\n handler: PermissionAwareEndpointHandler<TBody, TQuery, TParams, TResponse>;\n description?: string;\n}): CustomEndpointDefinition<TBody, TQuery, TParams, TResponse> {\n // Internal conversion: permission-aware handler is compatible with standard handler\n // The permission fields will be injected by the registration logic\n return definition as any;\n}\n\n// --- registerCustomEndpoints (from endpointRegistration.ts) ---\n\n/**\n * Registers custom endpoints on a Fastify instance.\n * Handles Zod validation, error responses, and type-safe request mapping.\n *\n * @param app - Fastify instance to register endpoints on\n * @param basePath - Base path for all endpoints (e.g., \"/\" or \"/api\")\n * @param endpoints - Array of custom endpoint definitions\n * @param options - Optional configuration for endpoint registration\n *\n * @example\n * ```typescript\n * registerCustomEndpoints(app, \"/api\", [\n * defineEndpoint({\n * method: \"GET\",\n * path: \"/users\",\n * querySchema: z.object({ limit: z.coerce.number() }),\n * handler: async (req) => ({ users: [] }),\n * }),\n * ]);\n * ```\n */\nexport function registerCustomEndpoints(\n app: FastifyInstance,\n basePath: string,\n endpoints: CustomEndpointDefinition[],\n options?: RegisterCustomEndpointsOptions\n): void {\n // Built-in MCP paths that should not be overridden\n const reservedPaths = [\"/mcp\", \"/healthz\", \"/tools\", \"/.well-known/mcp-config\"];\n\n for (const endpoint of endpoints) {\n const fullPath = `${basePath}${endpoint.path}`;\n\n // Check for path conflicts with built-in endpoints\n const isReserved = reservedPaths.some((reserved) =>\n fullPath.startsWith(`${basePath}${reserved}`)\n );\n\n if (isReserved) {\n console.warn(\n `Custom endpoint ${endpoint.method} ${endpoint.path} conflicts with built-in MCP endpoint. Skipping registration.`\n );\n continue;\n }\n\n // Convert method to lowercase for Fastify\n const method = endpoint.method.toLowerCase() as\n | \"get\"\n | \"post\"\n | \"put\"\n | \"delete\"\n | \"patch\";\n\n // Register the endpoint with Fastify\n app[method](fullPath, async (req: FastifyRequest, reply: FastifyReply) => {\n try {\n // Extract client ID from header or generate anonymous ID\n const clientIdHeader = (req.headers[\"mcp-client-id\"] as string)?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0\n ? clientIdHeader\n : `anon-${randomUUID()}`;\n\n // Validate request body if schema provided\n let body: any = undefined;\n if (endpoint.bodySchema) {\n const bodyResult = endpoint.bodySchema.safeParse(req.body);\n if (!bodyResult.success) {\n return createValidationError(reply, \"body\", bodyResult.error);\n }\n body = bodyResult.data;\n }\n\n // Validate query parameters if schema provided\n let query: any = {};\n if (endpoint.querySchema) {\n const queryResult = endpoint.querySchema.safeParse(req.query);\n if (!queryResult.success) {\n return createValidationError(reply, \"query\", queryResult.error);\n }\n query = queryResult.data;\n }\n\n // Validate path parameters if schema provided\n let params: any = {};\n if (endpoint.paramsSchema) {\n const paramsResult = endpoint.paramsSchema.safeParse(req.params);\n if (!paramsResult.success) {\n return createValidationError(reply, \"params\", paramsResult.error);\n }\n params = paramsResult.data;\n }\n\n // Build request object with validated data\n const customRequest: CustomEndpointRequest = {\n body,\n query,\n params,\n headers: req.headers as Record<string, string | string[] | undefined>,\n clientId,\n };\n\n // Merge additional context if provided (e.g., permissions from contextExtractor)\n if (options?.contextExtractor) {\n const additionalContext = await options.contextExtractor(req);\n Object.assign(customRequest, additionalContext);\n }\n\n // Call the user-defined handler with validated and typed data\n const result = await endpoint.handler(customRequest as any);\n\n // Validate response if schema provided\n if (endpoint.responseSchema) {\n const responseResult = endpoint.responseSchema.safeParse(result);\n if (!responseResult.success) {\n // Log the validation error for debugging\n console.error(\n `Response validation failed for ${endpoint.method} ${endpoint.path}:`,\n responseResult.error\n );\n\n // Return generic error to prevent information leakage\n reply.code(500);\n return {\n error: {\n code: \"RESPONSE_VALIDATION_ERROR\",\n message: \"Internal server error: invalid response format\",\n },\n } as EndpointErrorResponse;\n }\n // Return validated response data\n return responseResult.data;\n }\n\n // No response validation - return result as-is\n return result;\n } catch (error) {\n // Handle any errors thrown by the handler\n console.error(\n `Error in custom endpoint ${endpoint.method} ${endpoint.path}:`,\n error\n );\n\n reply.code(500);\n return {\n error: {\n code: \"INTERNAL_ERROR\",\n message:\n error instanceof Error ? error.message : \"Internal server error\",\n },\n } as EndpointErrorResponse;\n }\n });\n }\n}\n\n/**\n * Creates a standardized validation error response.\n * @param reply - Fastify reply object\n * @param field - The field that failed validation\n * @param error - Zod validation error\n * @returns Formatted error response\n */\nexport function createValidationError(\n reply: FastifyReply,\n field: string,\n error: z.ZodError\n): EndpointErrorResponse {\n reply.code(400);\n return {\n error: {\n code: \"VALIDATION_ERROR\",\n message: `Validation failed for ${field}`,\n details: error.errors,\n },\n };\n}\n","import Fastify, {\n type FastifyInstance,\n type FastifyReply,\n type FastifyRequest,\n} from \"fastify\";\nimport cors from \"@fastify/cors\";\nimport { randomUUID } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { DynamicToolManager } from \"../core/DynamicToolManager.js\";\nimport type { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport { ClientResourceCache } from \"../session/ClientResourceCache.js\";\nimport type { SessionContextResolver } from \"../session/SessionContextResolver.js\";\nimport type { SessionRequestContext } from \"../types/index.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type {\n FastifyTransportOptions,\n CreateBundleCallback,\n CustomEndpointDefinition,\n} from \"./http.types.js\";\nimport { registerCustomEndpoints } from \"./http.utils.js\";\n\nconst mcpClientIdSchema = z\n .string({ message: \"Missing required mcp-client-id header\" })\n .trim()\n .min(1, \"mcp-client-id header must not be empty\");\n\nexport class FastifyTransport {\n private readonly options: {\n host: string;\n port: number;\n basePath: string;\n cors: boolean;\n logger: boolean;\n app?: FastifyInstance;\n customEndpoints?: CustomEndpointDefinition[];\n };\n private readonly defaultManager: DynamicToolManager;\n private readonly createBundle: CreateBundleCallback;\n private readonly sessionContextResolver?: SessionContextResolver;\n private readonly baseContext?: unknown;\n private app: FastifyInstance | null = null;\n private readonly configSchema?: object;\n\n // Per-client server bundles and per-client session transports\n private readonly clientCache = new ClientResourceCache<{\n server: McpServer;\n orchestrator: ServerOrchestrator;\n sessions: Map<string, StreamableHTTPServerTransport>;\n }>({\n onEvict: (_key, bundle) => {\n // Clean up all sessions when a client bundle is evicted\n this.cleanupBundle(bundle);\n },\n });\n\n constructor(\n defaultManager: DynamicToolManager,\n createBundle: CreateBundleCallback,\n options: FastifyTransportOptions = {},\n configSchema?: object,\n sessionContextResolver?: SessionContextResolver,\n baseContext?: unknown\n ) {\n this.defaultManager = defaultManager;\n this.createBundle = createBundle;\n this.sessionContextResolver = sessionContextResolver;\n this.baseContext = baseContext;\n this.options = {\n host: options.host ?? \"0.0.0.0\",\n port: options.port ?? 3000,\n basePath: options.basePath ?? \"/\",\n cors: options.cors ?? true,\n logger: options.logger ?? false,\n app: options.app,\n customEndpoints: options.customEndpoints,\n };\n this.configSchema = configSchema;\n }\n\n static builder() {\n let _defaultManager: DynamicToolManager;\n let _createBundle: CreateBundleCallback;\n const opts: FastifyTransportOptions = {};\n let _configSchema: object | undefined;\n let _sessionContextResolver: SessionContextResolver | undefined;\n let _baseContext: unknown;\n const builder = {\n defaultManager(value: DynamicToolManager) { _defaultManager = value; return builder; },\n createBundle(value: CreateBundleCallback) { _createBundle = value; return builder; },\n host(value: string) { opts.host = value; return builder; },\n port(value: number) { opts.port = value; return builder; },\n basePath(value: string) { opts.basePath = value; return builder; },\n cors(value: boolean) { opts.cors = value; return builder; },\n logger(value: boolean) { opts.logger = value; return builder; },\n app(value: FastifyInstance) { opts.app = value; return builder; },\n customEndpoints(value: CustomEndpointDefinition[]) { opts.customEndpoints = value; return builder; },\n configSchema(value: object) { _configSchema = value; return builder; },\n sessionContextResolver(value: SessionContextResolver) { _sessionContextResolver = value; return builder; },\n baseContext(value: unknown) { _baseContext = value; return builder; },\n build() { return new FastifyTransport(_defaultManager, _createBundle, opts, _configSchema, _sessionContextResolver, _baseContext); },\n };\n return builder;\n }\n\n public async start(): Promise<void> {\n if (this.app) return;\n const app = this.options.app ?? Fastify({ logger: this.options.logger });\n if (this.options.cors) {\n await app.register(cors, { origin: true });\n }\n\n const base = this.normalizeBasePath(this.options.basePath);\n\n this.registerHealthEndpoint(app, base);\n this.registerToolsEndpoint(app, base);\n this.registerConfigDiscoveryEndpoint(app, base);\n this.registerMcpPostEndpoint(app, base);\n this.registerMcpGetEndpoint(app, base);\n this.registerMcpDeleteEndpoint(app, base);\n\n // Register custom endpoints if provided\n if (this.options.customEndpoints && this.options.customEndpoints.length > 0) {\n registerCustomEndpoints(app, base, this.options.customEndpoints);\n }\n\n // Only listen if we created the app\n if (!this.options.app) {\n await app.listen({ host: this.options.host, port: this.options.port });\n }\n this.app = app;\n }\n\n /**\n * @param basePath - The base path to normalize\n * @returns Normalized base path without trailing slash\n */\n private normalizeBasePath(basePath: string): string {\n return basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n private registerHealthEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/healthz`, async () => ({ ok: true }));\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n private registerToolsEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/tools`, async () => this.defaultManager.getStatus());\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n private registerConfigDiscoveryEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/.well-known/mcp-config`, async (_req, reply) => {\n reply.header(\"Content-Type\", \"application/schema+json; charset=utf-8\");\n const baseSchema = this.configSchema ?? {\n $schema: \"https://json-schema.org/draft/2020-12/schema\",\n title: \"MCP Session Configuration\",\n description: \"Schema for the /mcp endpoint configuration\",\n type: \"object\",\n properties: {},\n required: [],\n \"x-mcp-version\": \"1.0\",\n \"x-query-style\": \"dot+bracket\",\n };\n return baseSchema;\n });\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n private registerMcpPostEndpoint(app: FastifyInstance, base: string): void {\n app.post(\n `${base}/mcp`,\n async (req: FastifyRequest, reply: FastifyReply) => {\n const parseResult = mcpClientIdSchema.safeParse(\n req.headers[\"mcp-client-id\"]\n );\n if (!parseResult.success) {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32600, message: parseResult.error.issues[0].message },\n id: null,\n };\n }\n const clientId = parseResult.data;\n\n // Build session request context and resolve merged context\n const { cacheKey, mergedContext } = this.resolveSessionContext(\n req,\n clientId\n );\n\n let bundle = this.clientCache.get(cacheKey);\n if (!bundle) {\n const created = this.createBundle(mergedContext);\n bundle = {\n server: created.server,\n orchestrator: created.orchestrator,\n sessions: new Map(),\n };\n this.clientCache.set(cacheKey, bundle);\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n\n let transport: StreamableHTTPServerTransport | undefined;\n if (sessionId && bundle.sessions.get(sessionId)) {\n transport = bundle.sessions.get(sessionId)!;\n } else if (!sessionId && isInitializeRequest((req as any).body)) {\n const newSessionId = randomUUID();\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => newSessionId,\n onsessioninitialized: (sid: string) => {\n bundle!.sessions.set(sid, transport!);\n },\n });\n try {\n await bundle.server.connect(transport);\n } catch (error) {\n reply.code(500);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Error initializing server.\" },\n id: null,\n };\n }\n transport.onclose = () => {\n if (transport?.sessionId)\n bundle!.sessions.delete(transport.sessionId);\n };\n } else {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Session not found or expired\" },\n id: null,\n };\n }\n\n // Delegate handling to SDK transport using raw Node req/res\n await transport.handleRequest(\n (req as any).raw,\n (reply as any).raw,\n (req as any).body\n );\n // Fastify will consider the response already sent by transport\n return reply;\n }\n );\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n private registerMcpGetEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/mcp`, async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0 ? clientIdHeader : \"\";\n if (!clientId) {\n reply.code(400);\n return \"Missing mcp-client-id\";\n }\n const { cacheKey } = this.resolveSessionContext(req, clientId);\n const bundle = this.clientCache.get(cacheKey);\n if (!bundle) {\n reply.code(400);\n return \"Invalid or expired client\";\n }\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId) {\n reply.code(400);\n return \"Missing mcp-session-id\";\n }\n const transport = bundle.sessions.get(sessionId);\n if (!transport) {\n reply.code(400);\n return \"Invalid or expired session ID\";\n }\n await transport.handleRequest((req as any).raw, (reply as any).raw);\n return reply;\n });\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n private registerMcpDeleteEndpoint(app: FastifyInstance, base: string): void {\n app.delete(\n `${base}/mcp`,\n async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0 ? clientIdHeader : \"\";\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!clientId || !sessionId) {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32600,\n message: \"Missing mcp-client-id or mcp-session-id header\",\n },\n id: null,\n };\n }\n const { cacheKey } = this.resolveSessionContext(req, clientId);\n const bundle = this.clientCache.get(cacheKey);\n const transport = bundle?.sessions.get(sessionId);\n if (!bundle || !transport) {\n reply.code(404);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Session not found or expired\" },\n id: null,\n };\n }\n try {\n // Best-effort close and evict\n if (typeof (transport as any).close === \"function\") {\n try {\n await (transport as any).close();\n } catch {}\n }\n } finally {\n if (transport?.sessionId) bundle.sessions.delete(transport.sessionId);\n else bundle.sessions.delete(sessionId);\n }\n reply.code(204).send();\n return reply;\n }\n );\n }\n\n public async stop(): Promise<void> {\n if (!this.app) return;\n\n // Stop the cache pruning interval and clear all entries (triggers cleanup)\n this.clientCache.stop(true);\n\n if (!this.options.app) {\n await this.app.close();\n }\n this.app = null;\n }\n\n /**\n * @param bundle - The client bundle to clean up\n */\n private cleanupBundle(bundle: {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n sessions: Map<string, StreamableHTTPServerTransport>;\n }): void {\n for (const [sessionId, transport] of bundle.sessions.entries()) {\n try {\n if (typeof (transport as any).close === \"function\") {\n (transport as any).close().catch((err: unknown) => {\n console.warn(`Error closing session ${sessionId}:`, err);\n });\n }\n } catch (err) {\n console.warn(`Error closing session ${sessionId}:`, err);\n }\n }\n bundle.sessions.clear();\n }\n\n /**\n * @param req - The Fastify request\n * @param clientId - The client identifier\n * @returns Object with cache key and merged context\n */\n private resolveSessionContext(\n req: FastifyRequest,\n clientId: string\n ): { cacheKey: string; mergedContext: unknown } {\n // If no session context resolver, use simple clientId cache key\n if (!this.sessionContextResolver) {\n return {\n cacheKey: clientId,\n mergedContext: this.baseContext,\n };\n }\n\n // Build session request context\n const sessionRequestContext: SessionRequestContext = {\n clientId,\n headers: this.extractHeaders(req),\n query: this.extractQuery(req),\n };\n\n // Resolve the merged context\n const result = this.sessionContextResolver.resolve(\n sessionRequestContext,\n this.baseContext\n );\n\n // Build cache key: clientId:suffix\n const cacheKey =\n result.cacheKeySuffix === \"default\"\n ? clientId\n : `${clientId}:${result.cacheKeySuffix}`;\n\n return {\n cacheKey,\n mergedContext: result.context,\n };\n }\n\n /**\n * @param req - The Fastify request\n * @returns Headers as a string record\n */\n private extractHeaders(req: FastifyRequest): Record<string, string> {\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers[key.toLowerCase()] = value;\n } else if (Array.isArray(value) && value.length > 0) {\n headers[key.toLowerCase()] = value[0];\n }\n }\n return headers;\n }\n\n /**\n * @param req - The Fastify request\n * @returns Query parameters as a string record\n */\n private extractQuery(req: FastifyRequest): Record<string, string> {\n const query: Record<string, string> = {};\n const rawQuery = req.query as Record<string, unknown>;\n if (rawQuery && typeof rawQuery === \"object\") {\n for (const [key, value] of Object.entries(rawQuery)) {\n if (typeof value === \"string\") {\n query[key] = value;\n }\n }\n }\n return query;\n }\n}\n","import type {\n SessionContextConfig,\n SessionRequestContext,\n} from \"../types/index.js\";\nimport { createHash } from \"node:crypto\";\nimport type { SessionContextResult } from \"./session.types.js\";\n\nexport class SessionContextResolver {\n private readonly config: SessionContextConfig;\n private readonly queryParamName: string;\n private readonly encoding: \"base64\" | \"json\";\n private readonly allowedKeys: Set<string> | null;\n private readonly mergeStrategy: \"shallow\" | \"deep\";\n\n constructor(config: SessionContextConfig) {\n this.config = config;\n this.queryParamName = config.queryParam?.name ?? \"config\";\n this.encoding = config.queryParam?.encoding ?? \"base64\";\n this.allowedKeys = config.queryParam?.allowedKeys\n ? new Set(config.queryParam.allowedKeys)\n : null;\n this.mergeStrategy = config.merge ?? \"shallow\";\n }\n\n static builder() {\n const opts: Partial<SessionContextConfig> = {};\n const builder = {\n enabled(value: boolean) { opts.enabled = value; return builder; },\n queryParam(value: SessionContextConfig[\"queryParam\"]) { opts.queryParam = value; return builder; },\n contextResolver(value: SessionContextConfig[\"contextResolver\"]) { opts.contextResolver = value; return builder; },\n merge(value: \"shallow\" | \"deep\") { opts.merge = value; return builder; },\n build() { return new SessionContextResolver(opts as SessionContextConfig); },\n };\n return builder;\n }\n\n /**\n * @param request - The request context (clientId, headers, query)\n * @param baseContext - The base context from server configuration\n * @returns The resolved context and cache key suffix\n */\n resolve(\n request: SessionRequestContext,\n baseContext: unknown\n ): SessionContextResult {\n // If disabled, return base context with default cache key\n if (this.config.enabled === false) {\n return {\n context: baseContext,\n cacheKeySuffix: \"default\",\n };\n }\n\n const parsedConfig = this.parseQueryConfig(request.query);\n\n if (this.config.contextResolver) {\n return this.resolveWithCustomResolver(request, baseContext, parsedConfig);\n }\n\n return this.resolveWithDefaultMerge(baseContext, parsedConfig);\n }\n\n /**\n * @param request - The request context\n * @param baseContext - The base context from server configuration\n * @param parsedConfig - The parsed query parameter config\n * @returns The resolved context and cache key suffix\n */\n private resolveWithCustomResolver(\n request: SessionRequestContext,\n baseContext: unknown,\n parsedConfig: Record<string, unknown>\n ): SessionContextResult {\n const resolver = this.config.contextResolver;\n if (!resolver) {\n return { context: baseContext, cacheKeySuffix: \"default\" };\n }\n\n try {\n const resolvedContext = resolver(\n request,\n baseContext,\n parsedConfig\n );\n return {\n context: resolvedContext,\n cacheKeySuffix: this.generateCacheKeySuffix(parsedConfig),\n };\n } catch {\n // Fail secure: return base context on resolver error\n return {\n context: baseContext,\n cacheKeySuffix: \"default\",\n };\n }\n }\n\n /**\n * @param baseContext - The base context from server configuration\n * @param parsedConfig - The parsed query parameter config\n * @returns The merged context and cache key suffix\n */\n private resolveWithDefaultMerge(\n baseContext: unknown,\n parsedConfig: Record<string, unknown>\n ): SessionContextResult {\n const mergedContext = this.mergeContexts(baseContext, parsedConfig);\n return {\n context: mergedContext,\n cacheKeySuffix: this.generateCacheKeySuffix(parsedConfig),\n };\n }\n\n /**\n * @param query - Query parameters from the request\n * @returns Parsed and filtered config object\n */\n private parseQueryConfig(\n query: Record<string, string>\n ): Record<string, unknown> {\n const rawValue = query[this.queryParamName];\n if (!rawValue) {\n return {};\n }\n\n try {\n let jsonString: string;\n\n if (this.encoding === \"base64\") {\n // Decode base64 to JSON string\n jsonString = Buffer.from(rawValue, \"base64\").toString(\"utf-8\");\n } else {\n // JSON encoding - value should already be JSON string\n jsonString = rawValue;\n }\n\n const parsed = JSON.parse(jsonString);\n\n // Must be an object\n if (typeof parsed !== \"object\" || parsed === null || Array.isArray(parsed)) {\n return {};\n }\n\n // Filter allowed keys if whitelist is configured\n return this.filterAllowedKeys(parsed);\n } catch {\n // Fail secure: return empty object on any parse error\n return {};\n }\n }\n\n /**\n * @param parsed - The parsed config object\n * @returns Filtered config with only allowed keys\n */\n private filterAllowedKeys(\n parsed: Record<string, unknown>\n ): Record<string, unknown> {\n if (!this.allowedKeys) {\n return parsed;\n }\n\n const filtered: Record<string, unknown> = {};\n for (const key of this.allowedKeys) {\n if (key in parsed) {\n filtered[key] = parsed[key];\n }\n }\n return filtered;\n }\n\n /**\n * @param baseContext - The base context from server configuration\n * @param sessionConfig - The parsed session config\n * @returns Merged context\n */\n private mergeContexts(\n baseContext: unknown,\n sessionConfig: Record<string, unknown>\n ): unknown {\n // If no session config, return base context as-is\n if (Object.keys(sessionConfig).length === 0) {\n return baseContext;\n }\n\n // If base context is not an object, session config takes precedence\n if (\n typeof baseContext !== \"object\" ||\n baseContext === null ||\n Array.isArray(baseContext)\n ) {\n return sessionConfig;\n }\n\n if (this.mergeStrategy === \"deep\") {\n return this.deepMerge(\n baseContext as Record<string, unknown>,\n sessionConfig\n );\n }\n\n // Shallow merge: session config overrides base context\n return {\n ...(baseContext as Record<string, unknown>),\n ...sessionConfig,\n };\n }\n\n /**\n * @param base - The base object\n * @param override - The override object\n * @returns Deep merged object\n */\n private deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>\n ): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base };\n\n for (const [key, value] of Object.entries(override)) {\n const baseValue = result[key];\n\n if (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n typeof baseValue === \"object\" &&\n baseValue !== null &&\n !Array.isArray(baseValue)\n ) {\n // Both are objects - deep merge\n result[key] = this.deepMerge(\n baseValue as Record<string, unknown>,\n value as Record<string, unknown>\n );\n } else {\n // Override base value\n result[key] = value;\n }\n }\n\n return result;\n }\n\n /**\n * @param sessionConfig - The parsed session config\n * @returns Hash string or 'default'\n */\n private generateCacheKeySuffix(\n sessionConfig: Record<string, unknown>\n ): string {\n if (Object.keys(sessionConfig).length === 0) {\n return \"default\";\n }\n\n // Sort keys for deterministic hash\n const sortedKeys = Object.keys(sessionConfig).sort();\n const normalizedObj: Record<string, unknown> = {};\n for (const key of sortedKeys) {\n normalizedObj[key] = sessionConfig[key];\n }\n\n const jsonString = JSON.stringify(normalizedObj);\n return createHash(\"sha256\").update(jsonString).digest(\"hex\").slice(0, 16);\n }\n}\n","import type { SessionContextConfig } from \"../types/index.js\";\n\n/**\n * Validates a session context configuration object to ensure it meets all requirements.\n * Throws descriptive errors for any validation failures.\n *\n * @param config - The session context configuration to validate\n */\nexport function validateSessionContextConfig(config: SessionContextConfig): void {\n validateConfigExists(config);\n validateEnabledField(config);\n validateQueryParamConfig(config);\n validateContextResolver(config);\n validateMergeStrategy(config);\n}\n\n/**\n * @param config - The session context configuration to validate\n */\nfunction validateConfigExists(config: SessionContextConfig): void {\n if (!config || typeof config !== \"object\") {\n throw new Error(\n \"Session context configuration must be an object\"\n );\n }\n}\n\n/**\n * @param config - The session context configuration to validate\n */\nfunction validateEnabledField(config: SessionContextConfig): void {\n if (config.enabled === undefined) {\n return;\n }\n\n if (typeof config.enabled !== \"boolean\") {\n throw new Error(\n `enabled must be a boolean, got ${typeof config.enabled}`\n );\n }\n}\n\n/**\n * @param config - The session context configuration to validate\n */\nfunction validateQueryParamConfig(config: SessionContextConfig): void {\n if (config.queryParam === undefined) {\n return;\n }\n\n if (typeof config.queryParam !== \"object\" || config.queryParam === null) {\n throw new Error(\"queryParam must be an object\");\n }\n\n // Validate name\n if (config.queryParam.name !== undefined) {\n if (\n typeof config.queryParam.name !== \"string\" ||\n config.queryParam.name.length === 0\n ) {\n throw new Error(\"queryParam.name must be a non-empty string\");\n }\n }\n\n // Validate encoding\n if (config.queryParam.encoding !== undefined) {\n if (\n config.queryParam.encoding !== \"base64\" &&\n config.queryParam.encoding !== \"json\"\n ) {\n throw new Error(\n `Invalid queryParam.encoding: \"${config.queryParam.encoding}\". Must be \"base64\" or \"json\"`\n );\n }\n }\n\n // Validate allowedKeys\n if (config.queryParam.allowedKeys !== undefined) {\n if (!Array.isArray(config.queryParam.allowedKeys)) {\n throw new Error(\"queryParam.allowedKeys must be an array of strings\");\n }\n\n for (let i = 0; i < config.queryParam.allowedKeys.length; i++) {\n const key = config.queryParam.allowedKeys[i];\n if (typeof key !== \"string\" || key.length === 0) {\n throw new Error(\n `queryParam.allowedKeys[${i}] must be a non-empty string`\n );\n }\n }\n }\n}\n\n/**\n * @param config - The session context configuration to validate\n */\nfunction validateContextResolver(config: SessionContextConfig): void {\n if (config.contextResolver === undefined) {\n return;\n }\n\n if (typeof config.contextResolver !== \"function\") {\n throw new Error(\n \"contextResolver must be a function: (request, baseContext, parsedQueryConfig?) => unknown\"\n );\n }\n}\n\n/**\n * @param config - The session context configuration to validate\n */\nfunction validateMergeStrategy(config: SessionContextConfig): void {\n if (config.merge === undefined) {\n return;\n }\n\n if (config.merge !== \"shallow\" && config.merge !== \"deep\") {\n throw new Error(\n `Invalid merge strategy: \"${config.merge}\". Must be \"shallow\" or \"deep\"`\n );\n }\n}\n","import { z } from \"zod\";\nimport type { Mode } from \"../types/index.js\";\n\n/**\n * Zod schema for validating startup configuration.\n * Uses strict mode to reject unknown properties like 'initialToolsets'.\n */\nexport const startupConfigSchema = z\n .object({\n mode: z.enum([\"DYNAMIC\", \"STATIC\"]).optional(),\n toolsets: z.union([z.array(z.string()), z.literal(\"ALL\")]).optional(),\n })\n .strict();\n\n/**\n * Validates a startup configuration object against `startupConfigSchema`.\n * Throws a descriptive error when the config is invalid.\n *\n * @param startup - The startup configuration to validate\n */\nexport function validateStartupConfig(\n startup: { mode?: Exclude<Mode, \"ALL\">; toolsets?: string[] | \"ALL\" }\n): void {\n try {\n startupConfigSchema.parse(startup);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const formatted = error.format();\n throw new Error(\n `Invalid startup configuration:\\n${JSON.stringify(formatted, null, 2)}\\n\\n` +\n `Hint: Common mistake - use \"toolsets\" not \"initialToolsets\"`\n );\n }\n throw error;\n }\n}\n\n/**\n * Creates a notifier function that sends `tools/list_changed` notifications\n * to an MCP server. Handles two different notification APIs and suppresses\n * \"Not connected\" errors that occur when no clients are connected.\n *\n * @returns A function that sends tools/list_changed notifications to an MCP server\n */\nexport function createToolsChangedNotifier(): (target: unknown) => Promise<void> {\n type NotifierA = {\n server: { notification: (msg: { method: string }) => Promise<void> | void };\n };\n type NotifierB = { notifyToolsListChanged: () => Promise<void> | void };\n\n const hasNotifierA = (s: unknown): s is NotifierA =>\n typeof (s as NotifierA)?.server?.notification === \"function\";\n const hasNotifierB = (s: unknown): s is NotifierB =>\n typeof (s as NotifierB)?.notifyToolsListChanged === \"function\";\n\n return async (target: unknown) => {\n try {\n if (hasNotifierA(target)) {\n await target.server.notification({\n method: \"notifications/tools/list_changed\",\n });\n return;\n }\n if (hasNotifierB(target)) {\n await target.notifyToolsListChanged();\n }\n } catch (err) {\n // Suppress \"Not connected\" errors - expected when no clients are connected\n const errorMessage = err instanceof Error ? err.message : String(err);\n if (errorMessage === \"Not connected\") {\n return; // Silently ignore - no clients to notify\n }\n // Log other errors as they indicate actual problems\n console.warn(\"Failed to send tools list changed notification:\", err);\n }\n };\n}\n\n/**\n * Resolves whether meta-tools should be registered.\n * When `explicit` is provided it takes precedence; otherwise meta-tools are\n * enabled in DYNAMIC mode and disabled in STATIC mode.\n *\n * @param explicit - The user-provided registerMetaTools value (undefined = auto)\n * @param mode - The resolved server mode\n * @returns Whether meta-tools should be registered\n */\nexport function resolveMetaToolsFlag(\n explicit: boolean | undefined,\n mode: Exclude<Mode, \"ALL\">\n): boolean {\n return explicit !== undefined ? explicit : mode === \"DYNAMIC\";\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { Mode } from \"../types/index.js\";\nimport type { CreateBundleCallback } from \"../http/http.types.js\";\nimport type { CreateMcpServerOptions, McpServerHandle } from \"./server.types.js\";\nimport { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport { FastifyTransport } from \"../http/FastifyTransport.js\";\nimport { SessionContextResolver } from \"../session/SessionContextResolver.js\";\nimport { validateSessionContextConfig } from \"../session/session.utils.js\";\nimport {\n validateStartupConfig,\n createToolsChangedNotifier,\n resolveMetaToolsFlag,\n} from \"./server.utils.js\";\n\nexport type { CreateMcpServerOptions } from \"./server.types.js\";\n\nexport async function createMcpServer(\n options: CreateMcpServerOptions\n): Promise<McpServerHandle> {\n // --- Validate ---\n validateOptions(options);\n\n const mode: Exclude<Mode, \"ALL\"> = options.startup?.mode ?? \"DYNAMIC\";\n const shouldRegisterMetaTools = resolveMetaToolsFlag(options.registerMetaTools, mode);\n const sessionContextResolver = buildSessionContextResolver(options, mode);\n const notifyToolsChanged = createToolsChangedNotifier();\n\n // --- Build base server & orchestrator ---\n const baseServer: McpServer = options.createServer();\n const baseOrchestrator = buildOrchestrator(\n baseServer, options, mode, shouldRegisterMetaTools, notifyToolsChanged\n );\n\n if (mode === \"STATIC\") {\n await baseOrchestrator.ensureReady();\n }\n\n // --- Build transport ---\n const bundleFactory = createBundleFactory(\n options, mode, baseServer, baseOrchestrator, shouldRegisterMetaTools, notifyToolsChanged\n );\n const transport = buildTransport(\n options, baseOrchestrator.getManager(), bundleFactory, sessionContextResolver\n );\n\n return {\n server: baseServer,\n start: () => transport.start(),\n close: () => transport.stop(),\n };\n}\n\n// ---------------------------------------------------------------------------\n// Named helper functions\n// ---------------------------------------------------------------------------\n\n/**\n * Consolidates all upfront validation guards.\n *\n * @param options - Server creation options to validate\n */\nfunction validateOptions(options: CreateMcpServerOptions): void {\n if (options.startup) {\n validateStartupConfig(options.startup);\n }\n if (typeof options.createServer !== \"function\") {\n throw new Error(\"createMcpServer: `createServer` (factory) is required\");\n }\n}\n\n/**\n * Validates session context config, builds the resolver, and warns about\n * limited utility in STATIC mode.\n *\n * @param options - Server creation options containing sessionContext config\n * @param mode - The resolved server mode\n * @returns A SessionContextResolver if configured, otherwise undefined\n */\nfunction buildSessionContextResolver(\n options: CreateMcpServerOptions,\n mode: Exclude<Mode, \"ALL\">\n): SessionContextResolver | undefined {\n if (!options.sessionContext) return undefined;\n\n validateSessionContextConfig(options.sessionContext);\n\n const resolver = SessionContextResolver.builder()\n .enabled(options.sessionContext.enabled ?? true)\n .queryParam(options.sessionContext.queryParam)\n .contextResolver(options.sessionContext.contextResolver)\n .merge(options.sessionContext.merge ?? \"shallow\")\n .build();\n\n if (mode === \"STATIC\" && options.sessionContext.enabled !== false) {\n console.warn(\n \"sessionContext has limited effect in STATIC mode: all clients share the same server instance with base context. \" +\n \"Use DYNAMIC mode for per-session context isolation.\"\n );\n }\n\n return resolver;\n}\n\n/**\n * Builds a ServerOrchestrator with the standard configuration. Used once for\n * the base orchestrator and once per DYNAMIC client.\n *\n * @param server - The MCP server instance\n * @param options - Server creation options (catalog, moduleLoaders, exposurePolicy, startup)\n * @param mode - The resolved server mode\n * @param shouldRegisterMetaTools - Pre-resolved meta-tools flag\n * @param notifyToolsChanged - Notifier function for tool list changes\n * @param context - Optional context override (defaults to options.context)\n * @returns A configured ServerOrchestrator\n */\nfunction buildOrchestrator(\n server: McpServer,\n options: CreateMcpServerOptions,\n mode: Exclude<Mode, \"ALL\">,\n shouldRegisterMetaTools: boolean,\n notifyToolsChanged: (target: unknown) => Promise<void>,\n context?: unknown\n): ServerOrchestrator {\n const builder = ServerOrchestrator.builder()\n .server(server)\n .catalog(options.catalog)\n .moduleLoaders(options.moduleLoaders ?? {})\n .context(context !== undefined ? context : options.context)\n .notifyToolsListChanged(async () => notifyToolsChanged(server))\n .registerMetaTools(shouldRegisterMetaTools);\n\n if (options.exposurePolicy) {\n builder.exposurePolicy(options.exposurePolicy);\n }\n if (options.startup) {\n builder.startup(options.startup);\n }\n\n return builder.build();\n}\n\n/**\n * Creates the bundle factory callback for the transport layer.\n * In STATIC mode all clients share one server + orchestrator.\n * In DYNAMIC mode a fresh server + orchestrator is created per client.\n *\n * @param options - Server creation options\n * @param mode - STATIC reuses base bundle; DYNAMIC creates fresh per client\n * @param baseServer - The shared base server instance\n * @param baseOrchestrator - The shared base orchestrator\n * @param shouldRegisterMetaTools - Pre-resolved meta-tools flag\n * @param notifyToolsChanged - Notifier function for tool list changes\n * @returns Bundle factory callback for the transport layer\n */\nfunction createBundleFactory(\n options: CreateMcpServerOptions,\n mode: Exclude<Mode, \"ALL\">,\n baseServer: McpServer,\n baseOrchestrator: ServerOrchestrator,\n shouldRegisterMetaTools: boolean,\n notifyToolsChanged: (target: unknown) => Promise<void>\n): CreateBundleCallback {\n return (mergedContext?: unknown) => {\n if (mode === \"STATIC\") {\n // STATIC: all clients share one server + orchestrator\n return { server: baseServer, orchestrator: baseOrchestrator };\n }\n\n // DYNAMIC: fresh server + orchestrator per client\n const effectiveContext = mergedContext ?? options.context;\n const clientServer: McpServer = options.createServer();\n const clientOrchestrator = buildOrchestrator(\n clientServer, options, mode, shouldRegisterMetaTools, notifyToolsChanged, effectiveContext\n );\n return { server: clientServer, orchestrator: clientOrchestrator };\n };\n}\n\n/**\n * Builds the FastifyTransport using the builder pattern, handling conditional\n * `.app()`, `.customEndpoints()`, `.sessionContextResolver()`, and `.baseContext()` chaining.\n *\n * @param options - Server creation options (http, configSchema, context)\n * @param manager - Default DynamicToolManager for status endpoints\n * @param bundleFactory - Bundle factory callback for the transport layer\n * @param sessionContextResolver - Optional session context resolver\n * @returns A configured FastifyTransport\n */\nfunction buildTransport(\n options: CreateMcpServerOptions,\n manager: ReturnType<ServerOrchestrator[\"getManager\"]>,\n bundleFactory: CreateBundleCallback,\n sessionContextResolver: SessionContextResolver | undefined\n): FastifyTransport {\n const builder = FastifyTransport.builder()\n .defaultManager(manager)\n .createBundle(bundleFactory)\n .host(options.http?.host ?? \"0.0.0.0\")\n .port(options.http?.port ?? 3000)\n .basePath(options.http?.basePath ?? \"/\")\n .cors(options.http?.cors ?? true)\n .logger(options.http?.logger ?? false);\n\n if (options.http?.app) {\n builder.app(options.http.app);\n }\n if (options.http?.customEndpoints) {\n builder.customEndpoints(options.http.customEndpoints);\n }\n if (options.configSchema) {\n builder.configSchema(options.configSchema);\n }\n if (sessionContextResolver) {\n builder.sessionContextResolver(sessionContextResolver);\n }\n if (options.context !== undefined) {\n builder.baseContext(options.context);\n }\n\n return builder.build();\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ExposurePolicy, PermissionConfig } from \"../types/index.js\";\nimport type { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport type { PermissionResolver } from \"./PermissionResolver.js\";\nimport type {\n ClientRequestContext,\n PermissionAwareBundle,\n} from \"./permissions.types.js\";\n\n// --- Validation functions (from validatePermissionConfig.ts) ---\n\n/**\n * Validates a permission configuration object to ensure it meets all requirements.\n * Throws descriptive errors for any validation failures.\n * @param config - The permission configuration to validate\n */\nexport function validatePermissionConfig(config: PermissionConfig): void {\n validateConfigExists(config);\n validateSourceField(config);\n validateConfigBasedPermissions(config);\n validateTypes(config);\n}\n\n/**\n * @param config - The permission configuration to validate\n */\nfunction validateConfigExists(config: PermissionConfig): void {\n if (!config || typeof config !== \"object\") {\n throw new Error(\n \"Permission configuration is required for createPermissionBasedMcpServer\"\n );\n }\n}\n\n/**\n * @param config - The permission configuration to validate\n */\nfunction validateSourceField(config: PermissionConfig): void {\n if (!config.source) {\n throw new Error('Permission source must be either \"headers\" or \"config\"');\n }\n\n if (config.source !== \"headers\" && config.source !== \"config\") {\n throw new Error(\n `Invalid permission source: \"${config.source}\". Must be either \"headers\" or \"config\"`\n );\n }\n}\n\n/**\n * @param config - The permission configuration to validate\n */\nfunction validateConfigBasedPermissions(config: PermissionConfig): void {\n if (config.source === \"config\") {\n if (!config.staticMap && !config.resolver) {\n throw new Error(\n \"Config-based permissions require at least one of: staticMap or resolver function\"\n );\n }\n }\n}\n\n/**\n * @param config - The permission configuration to validate\n */\nfunction validateTypes(config: PermissionConfig): void {\n if (config.staticMap !== undefined) {\n if (typeof config.staticMap !== \"object\" || config.staticMap === null) {\n throw new Error(\n \"staticMap must be an object mapping client IDs to toolset arrays\"\n );\n }\n\n // Validate that staticMap values are arrays\n validateStaticMapValues(config.staticMap);\n }\n\n if (config.resolver !== undefined) {\n if (typeof config.resolver !== \"function\") {\n throw new Error(\n \"resolver must be a synchronous function: (clientId: string) => string[]\"\n );\n }\n }\n\n if (config.defaultPermissions !== undefined) {\n if (!Array.isArray(config.defaultPermissions)) {\n throw new Error(\"defaultPermissions must be an array of toolset names\");\n }\n }\n\n if (config.headerName !== undefined) {\n if (typeof config.headerName !== \"string\" || config.headerName.length === 0) {\n throw new Error(\"headerName must be a non-empty string\");\n }\n }\n}\n\n/**\n * @param staticMap - The static map to validate\n */\nfunction validateStaticMapValues(staticMap: Record<string, string[]>): void {\n for (const [clientId, permissions] of Object.entries(staticMap)) {\n if (!Array.isArray(permissions)) {\n throw new Error(\n `staticMap value for client \"${clientId}\" must be an array of toolset names`\n );\n }\n }\n}\n\n// --- createPermissionAwareBundle (from createPermissionAwareBundle.ts) ---\n\n/**\n * Creates a permission-aware bundle creation function that wraps the original\n * createBundle function with permission resolution and enforcement.\n *\n * @param originalCreateBundle - Bundle creation function that accepts allowed toolsets\n * @param permissionResolver - Resolver instance for determining client permissions\n * @returns Enhanced bundle creation function that accepts client context\n */\nexport function createPermissionAwareBundle(\n originalCreateBundle: (allowedToolsets: string[]) => {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n },\n permissionResolver: PermissionResolver\n) {\n return async (\n context: ClientRequestContext\n ): Promise<PermissionAwareBundle> => {\n // Resolve permissions for this client\n const requestedToolsets = permissionResolver.resolvePermissions(\n context.clientId,\n context.headers\n );\n\n // Create bundle with allowed toolsets (STATIC mode pre-loads them)\n const bundle = originalCreateBundle(requestedToolsets);\n\n // Wait for toolsets to be enabled before returning\n // This ensures tools are registered before the server connects to transport\n const manager = bundle.orchestrator.getManager();\n\n const enabledToolsets: string[] = [];\n const failedToolsets: string[] = [];\n\n if (requestedToolsets.length > 0) {\n const result = await manager.enableToolsets(requestedToolsets);\n\n // Collect successful and failed toolsets\n for (const r of result.results) {\n if (r.success) {\n enabledToolsets.push(r.name);\n } else {\n failedToolsets.push(r.name);\n console.warn(\n `Failed to enable toolset '${r.name}' for client '${context.clientId}': ${r.message}`\n );\n }\n }\n\n // If ALL toolsets failed, this is likely a configuration error\n if (enabledToolsets.length === 0 && failedToolsets.length > 0) {\n throw new Error(\n `All requested toolsets failed to enable for client '${context.clientId}'. ` +\n `Requested: [${requestedToolsets.join(\", \")}]. ` +\n `Check that toolset names in permissions match the catalog.`\n );\n }\n }\n\n // Return bundle with resolved permissions\n return {\n server: bundle.server,\n orchestrator: bundle.orchestrator,\n allowedToolsets: enabledToolsets,\n failedToolsets,\n };\n };\n}\n\n// --- sanitizeExposurePolicyForPermissions (from createPermissionBasedMcpServer.ts) ---\n\n/**\n * Validates and sanitizes exposure policy for permission-based servers.\n * Certain policy options are not applicable or could conflict with permission-based access control.\n * @param policy - The original exposure policy\n * @returns Sanitized policy safe for permission-based servers\n */\nexport function sanitizeExposurePolicyForPermissions(\n policy?: ExposurePolicy\n): ExposurePolicy | undefined {\n if (!policy) return undefined;\n\n const sanitized: ExposurePolicy = {\n namespaceToolsWithSetKey: policy.namespaceToolsWithSetKey,\n };\n\n // Warn about ignored options\n if (policy.allowlist !== undefined) {\n console.warn(\n \"Permission-based servers: exposurePolicy.allowlist is ignored. \" +\n \"Allowed toolsets are determined by client permissions.\"\n );\n }\n if (policy.denylist !== undefined) {\n console.warn(\n \"Permission-based servers: exposurePolicy.denylist is ignored. \" +\n \"Use permission configuration to control toolset access.\"\n );\n }\n if (policy.maxActiveToolsets !== undefined) {\n console.warn(\n \"Permission-based servers: exposurePolicy.maxActiveToolsets is ignored. \" +\n \"Toolset count is determined by client permissions.\"\n );\n }\n if (policy.onLimitExceeded !== undefined) {\n console.warn(\n \"Permission-based servers: exposurePolicy.onLimitExceeded is ignored. \" +\n \"No toolset limits are enforced.\"\n );\n }\n\n return sanitized;\n}\n","import type { PermissionConfig } from \"../types/index.js\";\n\nexport class PermissionResolver {\n private cache = new Map<string, string[]>();\n private readonly normalizedHeaderName: string;\n\n constructor(private config: PermissionConfig) {\n // Pre-normalize header name to lowercase for case-insensitive matching\n this.normalizedHeaderName = (\n config.headerName || \"mcp-toolset-permissions\"\n ).toLowerCase();\n }\n\n static builder() {\n const opts: Partial<PermissionConfig> = {};\n const builder = {\n source(value: \"headers\" | \"config\") { opts.source = value; return builder; },\n headerName(value: string) { opts.headerName = value; return builder; },\n staticMap(value: Record<string, string[]>) { opts.staticMap = value; return builder; },\n resolver(value: (clientId: string) => string[]) { opts.resolver = value; return builder; },\n defaultPermissions(value: string[]) { opts.defaultPermissions = value; return builder; },\n build() { return new PermissionResolver(opts as PermissionConfig); },\n };\n return builder;\n }\n\n /**\n * @param clientId - The unique identifier for the client\n * @param headers - Optional request headers\n * @returns Array of toolset names the client is allowed to access\n */\n resolvePermissions(\n clientId: string,\n headers?: Record<string, string>\n ): string[] {\n // Check cache first for performance\n if (this.cache.has(clientId)) {\n return this.cache.get(clientId)!;\n }\n\n let permissions: string[];\n\n try {\n if (this.config.source === \"headers\") {\n permissions = this.#parseHeaderPermissions(headers);\n } else {\n permissions = this.#resolveConfigPermissions(clientId);\n }\n\n // Validate that permissions is an array\n if (!Array.isArray(permissions)) {\n console.warn(\n `Permission resolution returned non-array for client ${clientId}, using empty permissions`\n );\n permissions = [];\n }\n\n // Filter out invalid toolset names (empty strings, non-strings)\n permissions = permissions.filter(\n (name) => typeof name === \"string\" && name.trim().length > 0\n );\n } catch (error) {\n // Catch any unexpected errors and apply most restrictive permissions\n console.error(\n `Unexpected error resolving permissions for client ${clientId}:`,\n error\n );\n permissions = [];\n }\n\n // Cache the resolved permissions\n this.cache.set(clientId, permissions);\n return permissions;\n }\n\n /**\n * @param clientId - The client ID to invalidate\n */\n invalidateCache(clientId: string): void {\n this.cache.delete(clientId);\n }\n\n /**\n * @param headers - Request headers containing permission data\n * @returns Array of toolset names from headers\n */\n #parseHeaderPermissions(headers?: Record<string, string>): string[] {\n if (!headers) {\n return [];\n }\n\n // Find header value using case-insensitive lookup\n const headerValue = this.#findHeaderCaseInsensitive(\n headers,\n this.normalizedHeaderName\n );\n\n if (!headerValue) {\n return [];\n }\n\n try {\n // Parse comma-separated list, trim whitespace, and filter empty strings\n return headerValue\n .split(\",\")\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n } catch (error) {\n // Handle malformed headers gracefully\n console.warn(\n `Failed to parse permission header '${this.normalizedHeaderName}':`,\n error\n );\n return [];\n }\n }\n\n /**\n * @param headers - The headers object to search\n * @param normalizedKey - The lowercase key to search for\n * @returns The header value if found\n */\n #findHeaderCaseInsensitive(\n headers: Record<string, string>,\n normalizedKey: string\n ): string | undefined {\n // Fast path: check if key exists as-is (common case with Fastify's lowercased headers)\n if (headers[normalizedKey] !== undefined) {\n return headers[normalizedKey];\n }\n // Slow path: iterate and compare lowercase\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() === normalizedKey) {\n return value;\n }\n }\n return undefined;\n }\n\n /**\n * @param clientId - The unique identifier for the client\n * @returns Array of toolset names from configuration\n */\n #resolveConfigPermissions(clientId: string): string[] {\n // Try resolver function first (if provided)\n if (this.config.resolver) {\n const resolverResult = this.#tryResolverFunction(clientId);\n if (resolverResult !== null) {\n return resolverResult;\n }\n // Fall through to static map or default if resolver fails\n }\n\n // Fall back to static map (if provided)\n if (this.config.staticMap) {\n const staticResult = this.#lookupStaticMap(clientId);\n if (staticResult !== null) {\n return staticResult;\n }\n }\n\n // Final fallback to default permissions\n return this.config.defaultPermissions || [];\n }\n\n /**\n * @param clientId - The unique identifier for the client\n * @returns Array of toolset names if successful, null if resolver fails\n */\n #tryResolverFunction(clientId: string): string[] | null {\n try {\n const result = this.config.resolver!(clientId);\n if (Array.isArray(result)) {\n return result;\n }\n console.warn(\n `Permission resolver returned non-array for client ${clientId}, using fallback`\n );\n return null;\n } catch (error) {\n // Log message only, not full stack trace (this is expected fallback behavior)\n const message = error instanceof Error ? error.message : String(error);\n console.warn(\n `Permission resolver declined client ${clientId} (${message}), trying fallback`\n );\n return null;\n }\n }\n\n /**\n * @param clientId - The unique identifier for the client\n * @returns Array of toolset names if found, null if client not in map\n */\n #lookupStaticMap(clientId: string): string[] | null {\n const permissions = this.config.staticMap![clientId];\n if (permissions !== undefined) {\n return Array.isArray(permissions) ? permissions : [];\n }\n return null;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n}\n","import Fastify, {\n type FastifyInstance,\n type FastifyReply,\n type FastifyRequest,\n} from \"fastify\";\nimport cors from \"@fastify/cors\";\nimport { randomUUID } from \"node:crypto\";\nimport { z } from \"zod\";\nimport type { DynamicToolManager } from \"../core/DynamicToolManager.js\";\nimport { ClientResourceCache } from \"../session/ClientResourceCache.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport type {\n ClientRequestContext,\n PermissionAwareBundle,\n PermissionAwareFastifyTransportOptions,\n} from \"./permissions.types.js\";\nimport type { CustomEndpointDefinition } from \"../http/http.types.js\";\nimport { registerCustomEndpoints } from \"../http/http.utils.js\";\n\nconst mcpClientIdSchema = z\n .string({ message: \"Missing required mcp-client-id header\" })\n .trim()\n .min(1, \"mcp-client-id header must not be empty\");\n\nexport class PermissionAwareFastifyTransport {\n private readonly options: {\n host: string;\n port: number;\n basePath: string;\n cors: boolean;\n logger: boolean;\n app?: FastifyInstance;\n customEndpoints?: CustomEndpointDefinition[];\n };\n private readonly defaultManager: DynamicToolManager;\n private readonly createPermissionAwareBundle: (\n context: ClientRequestContext\n ) => Promise<PermissionAwareBundle>;\n private app: FastifyInstance | null = null;\n private readonly configSchema?: object;\n\n // Per-client server bundles and per-client session transports\n private readonly clientCache = new ClientResourceCache<{\n server: McpServer;\n orchestrator: ServerOrchestrator;\n sessions: Map<string, StreamableHTTPServerTransport>;\n allowedToolsets: string[];\n failedToolsets: string[];\n }>({\n onEvict: (_key, bundle) => {\n // Clean up all sessions when a client bundle is evicted\n this.#cleanupBundle(bundle);\n },\n });\n\n constructor(\n defaultManager: DynamicToolManager,\n createPermissionAwareBundle: (\n context: ClientRequestContext\n ) => Promise<PermissionAwareBundle>,\n options: PermissionAwareFastifyTransportOptions = {},\n configSchema?: object\n ) {\n this.defaultManager = defaultManager;\n this.createPermissionAwareBundle = createPermissionAwareBundle;\n this.options = {\n host: options.host ?? \"0.0.0.0\",\n port: options.port ?? 3000,\n basePath: options.basePath ?? \"/\",\n cors: options.cors ?? true,\n logger: options.logger ?? false,\n app: options.app,\n customEndpoints: options.customEndpoints,\n };\n this.configSchema = configSchema;\n }\n\n static builder() {\n let _defaultManager: DynamicToolManager;\n let _createPermissionAwareBundle: (context: ClientRequestContext) => Promise<PermissionAwareBundle>;\n const opts: PermissionAwareFastifyTransportOptions = {};\n let _configSchema: object | undefined;\n const builder = {\n defaultManager(value: DynamicToolManager) { _defaultManager = value; return builder; },\n createPermissionAwareBundle(value: (context: ClientRequestContext) => Promise<PermissionAwareBundle>) { _createPermissionAwareBundle = value; return builder; },\n host(value: string) { opts.host = value; return builder; },\n port(value: number) { opts.port = value; return builder; },\n basePath(value: string) { opts.basePath = value; return builder; },\n cors(value: boolean) { opts.cors = value; return builder; },\n logger(value: boolean) { opts.logger = value; return builder; },\n app(value: FastifyInstance) { opts.app = value; return builder; },\n customEndpoints(value: CustomEndpointDefinition[]) { opts.customEndpoints = value; return builder; },\n configSchema(value: object) { _configSchema = value; return builder; },\n build() { return new PermissionAwareFastifyTransport(_defaultManager, _createPermissionAwareBundle, opts, _configSchema); },\n };\n return builder;\n }\n\n public async start(): Promise<void> {\n if (this.app) return;\n const app = this.options.app ?? Fastify({ logger: this.options.logger });\n if (this.options.cors) {\n await app.register(cors, { origin: true });\n }\n\n const base = this.#normalizeBasePath(this.options.basePath);\n\n this.#registerHealthEndpoint(app, base);\n this.#registerToolsEndpoint(app, base);\n this.#registerConfigDiscoveryEndpoint(app, base);\n this.#registerMcpPostEndpoint(app, base);\n this.#registerMcpGetEndpoint(app, base);\n this.#registerMcpDeleteEndpoint(app, base);\n\n // Register custom endpoints if provided with permission context\n // IMPORTANT: Only register if customEndpoints is provided AND has items\n if (this.options.customEndpoints && this.options.customEndpoints.length > 0) {\n registerCustomEndpoints(app, base, this.options.customEndpoints, {\n contextExtractor: async (req) => {\n // Extract client context from request\n const context = this.#extractClientContext(req);\n\n // Resolve permissions for this client\n try {\n const bundle = await this.createPermissionAwareBundle(context);\n return {\n allowedToolsets: bundle.allowedToolsets,\n failedToolsets: bundle.failedToolsets,\n };\n } catch (error) {\n // If permission resolution fails, return empty permissions\n console.warn(\n `Permission resolution failed for custom endpoint: ${error}`\n );\n return {\n allowedToolsets: [],\n failedToolsets: [],\n };\n }\n },\n });\n }\n\n // Only listen if we created the app\n if (!this.options.app) {\n await app.listen({ host: this.options.host, port: this.options.port });\n }\n this.app = app;\n }\n\n public async stop(): Promise<void> {\n if (!this.app) return;\n\n // Stop the cache pruning interval and clear all entries (triggers cleanup)\n this.clientCache.stop(true);\n\n if (!this.options.app) {\n await this.app.close();\n }\n this.app = null;\n }\n\n /**\n * @param bundle - The client bundle to clean up\n */\n #cleanupBundle(bundle: {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n sessions: Map<string, StreamableHTTPServerTransport>;\n allowedToolsets: string[];\n failedToolsets: string[];\n }): void {\n for (const [sessionId, transport] of bundle.sessions.entries()) {\n try {\n if (typeof (transport as any).close === \"function\") {\n (transport as any).close().catch((err: unknown) => {\n console.warn(`Error closing session ${sessionId}:`, err);\n });\n }\n } catch (err) {\n console.warn(`Error closing session ${sessionId}:`, err);\n }\n }\n bundle.sessions.clear();\n }\n\n /**\n * @param basePath - The base path to normalize\n * @returns Normalized base path without trailing slash\n */\n #normalizeBasePath(basePath: string): string {\n return basePath.endsWith(\"/\") ? basePath.slice(0, -1) : basePath;\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n #registerHealthEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/healthz`, async () => ({ ok: true }));\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n #registerToolsEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/tools`, async () => this.defaultManager.getStatus());\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n #registerConfigDiscoveryEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/.well-known/mcp-config`, async (_req, reply) => {\n reply.header(\"Content-Type\", \"application/schema+json; charset=utf-8\");\n const baseSchema = this.configSchema ?? {\n $schema: \"https://json-schema.org/draft/2020-12/schema\",\n title: \"MCP Session Configuration\",\n description: \"Schema for the /mcp endpoint configuration\",\n type: \"object\",\n properties: {},\n required: [],\n \"x-mcp-version\": \"1.0\",\n \"x-query-style\": \"dot+bracket\",\n };\n return baseSchema;\n });\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n #registerMcpPostEndpoint(app: FastifyInstance, base: string): void {\n app.post(\n `${base}/mcp`,\n async (req: FastifyRequest, reply: FastifyReply) => {\n // Validate mcp-client-id header\n const parseResult = mcpClientIdSchema.safeParse(\n req.headers[\"mcp-client-id\"]\n );\n if (!parseResult.success) {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32600, message: parseResult.error.issues[0].message },\n id: null,\n };\n }\n\n // Extract client context from request\n const context = this.#extractClientContext(req);\n\n // Get or create permission-aware bundle for this client\n let bundle = this.clientCache.get(context.clientId);\n if (!bundle) {\n try {\n const created = await this.createPermissionAwareBundle(context);\n\n // Log any failed toolsets for debugging\n if (created.failedToolsets.length > 0) {\n console.warn(\n `Client ${context.clientId} had ${created.failedToolsets.length} toolsets fail to enable: ` +\n `[${created.failedToolsets.join(\", \")}]. ` +\n `Successfully enabled: [${created.allowedToolsets.join(\", \")}]`\n );\n }\n\n const providedSessions = (created as { sessions?: Map<string, StreamableHTTPServerTransport> }).sessions;\n bundle = {\n server: created.server,\n orchestrator: created.orchestrator,\n allowedToolsets: created.allowedToolsets,\n failedToolsets: created.failedToolsets,\n sessions:\n providedSessions instanceof Map ? providedSessions : new Map(),\n };\n this.clientCache.set(context.clientId, bundle);\n } catch (error) {\n // Handle permission resolution or bundle creation failures\n console.error(\n `Failed to create permission-aware bundle for client ${context.clientId}:`,\n error\n );\n reply.code(403);\n return this.#createSafeErrorResponse(\"Access denied\");\n }\n }\n\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n\n let transport: StreamableHTTPServerTransport | undefined;\n if (sessionId && bundle.sessions.get(sessionId)) {\n transport = bundle.sessions.get(sessionId)!;\n } else if (!sessionId && isInitializeRequest((req as any).body)) {\n const newSessionId = randomUUID();\n transport = new StreamableHTTPServerTransport({\n sessionIdGenerator: () => newSessionId,\n onsessioninitialized: (sid: string) => {\n bundle!.sessions.set(sid, transport!);\n },\n });\n try {\n await bundle.server.connect(transport);\n } catch (error) {\n reply.code(500);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32603, message: \"Error initializing server.\" },\n id: null,\n };\n }\n transport.onclose = () => {\n if (transport?.sessionId)\n bundle!.sessions.delete(transport.sessionId);\n };\n } else {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Session not found or expired\" },\n id: null,\n };\n }\n\n // Delegate handling to SDK transport using raw Node req/res\n await transport.handleRequest(\n (req as any).raw,\n (reply as any).raw,\n (req as any).body\n );\n return reply;\n }\n );\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n #registerMcpGetEndpoint(app: FastifyInstance, base: string): void {\n app.get(`${base}/mcp`, async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0 ? clientIdHeader : \"\";\n if (!clientId) {\n reply.code(400);\n return \"Missing mcp-client-id\";\n }\n const bundle = this.clientCache.get(clientId);\n if (!bundle) {\n reply.code(400);\n return \"Invalid or expired client\";\n }\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!sessionId) {\n reply.code(400);\n return \"Missing mcp-session-id\";\n }\n const transport = bundle.sessions.get(sessionId);\n if (!transport) {\n reply.code(400);\n return \"Invalid or expired session ID\";\n }\n await transport.handleRequest((req as any).raw, (reply as any).raw);\n return reply;\n });\n }\n\n /**\n * @param app - Fastify instance\n * @param base - Base path for routes\n */\n #registerMcpDeleteEndpoint(app: FastifyInstance, base: string): void {\n app.delete(\n `${base}/mcp`,\n async (req: FastifyRequest, reply: FastifyReply) => {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0 ? clientIdHeader : \"\";\n const sessionId = req.headers[\"mcp-session-id\"] as string | undefined;\n if (!clientId || !sessionId) {\n reply.code(400);\n return {\n jsonrpc: \"2.0\",\n error: {\n code: -32600,\n message: \"Missing mcp-client-id or mcp-session-id header\",\n },\n id: null,\n };\n }\n const bundle = this.clientCache.get(clientId);\n const transport = bundle?.sessions.get(sessionId);\n if (!bundle || !transport) {\n reply.code(404);\n return {\n jsonrpc: \"2.0\",\n error: { code: -32000, message: \"Session not found or expired\" },\n id: null,\n };\n }\n try {\n // Best-effort close and evict\n if (typeof (transport as any).close === \"function\") {\n try {\n await (transport as any).close();\n } catch {}\n }\n } finally {\n if (transport?.sessionId) bundle.sessions.delete(transport.sessionId);\n else bundle.sessions.delete(sessionId);\n }\n reply.code(204).send();\n return reply;\n }\n );\n }\n\n /**\n * @param req - Fastify request object\n * @returns Client request context with ID and headers\n */\n #extractClientContext(req: FastifyRequest): ClientRequestContext {\n const clientIdHeader = (\n req.headers[\"mcp-client-id\"] as string | undefined\n )?.trim();\n const clientId =\n clientIdHeader && clientIdHeader.length > 0\n ? clientIdHeader\n : `anon-${randomUUID()}`;\n\n // Convert headers to plain object for permission resolution\n const headers: Record<string, string> = {};\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers[key] = value;\n }\n }\n\n return { clientId, headers };\n }\n\n /**\n * @param message - Generic error message to return to client\n * @param code - JSON-RPC error code\n * @returns JSON-RPC error response object\n */\n #createSafeErrorResponse(message: string = \"Access denied\", code: number = -32000) {\n return {\n jsonrpc: \"2.0\" as const,\n error: {\n code,\n message,\n },\n id: null,\n };\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { ExposurePolicy } from \"../types/index.js\";\nimport type {\n CreatePermissionBasedMcpServerOptions,\n McpServerHandle,\n} from \"./server.types.js\";\nimport {\n validatePermissionConfig,\n createPermissionAwareBundle,\n sanitizeExposurePolicyForPermissions,\n} from \"../permissions/permissions.utils.js\";\nimport { validateSessionContextConfig } from \"../session/session.utils.js\";\nimport { PermissionResolver } from \"../permissions/PermissionResolver.js\";\nimport { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport { PermissionAwareFastifyTransport } from \"../permissions/PermissionAwareFastifyTransport.js\";\n\nexport async function createPermissionBasedMcpServer(\n options: CreatePermissionBasedMcpServerOptions\n): Promise<McpServerHandle> {\n // --- Validate ---\n validatePermissionOptions(options);\n\n const sanitizedPolicy = sanitizeExposurePolicyForPermissions(options.exposurePolicy);\n const permissionResolver = buildPermissionResolver(options);\n\n // --- Base server & status-only orchestrator ---\n const baseServer: McpServer = options.createServer();\n const baseOrchestrator = buildPermissionOrchestrator(baseServer, options, sanitizedPolicy);\n\n // --- Per-client bundle factory ---\n const createBundle = createPermissionAwareBundle(\n createClientOrchestratorFactory(options, sanitizedPolicy),\n permissionResolver\n );\n\n // --- Transport ---\n const transport = buildPermissionTransport(options, baseOrchestrator.getManager(), createBundle);\n\n return {\n server: baseServer,\n start: () => transport.start(),\n close: async () => {\n try {\n await transport.stop();\n } finally {\n permissionResolver.clearCache();\n }\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Named helper functions\n// ---------------------------------------------------------------------------\n\n/**\n * Consolidates all upfront validation guards for permission-based servers.\n *\n * @param options - Server creation options to validate\n */\nfunction validatePermissionOptions(\n options: CreatePermissionBasedMcpServerOptions\n): void {\n if (!options.permissions) {\n throw new Error(\n \"Permission configuration is required for createPermissionBasedMcpServer. \" +\n \"Please provide a 'permissions' field in the options.\"\n );\n }\n validatePermissionConfig(options.permissions);\n\n if (options.sessionContext) {\n validateSessionContextConfig(options.sessionContext);\n console.warn(\n \"Session context support for permission-based servers is limited. \" +\n \"The base context will be used for module loaders.\"\n );\n }\n\n if ((options as any).startup) {\n throw new Error(\n \"Permission-based servers determine toolsets from client permissions. \" +\n \"The 'startup' option is not allowed. Remove it from your configuration.\"\n );\n }\n\n if (typeof options.createServer !== \"function\") {\n throw new Error(\n \"createPermissionBasedMcpServer: `createServer` (factory) is required\"\n );\n }\n}\n\n/**\n * Builds a PermissionResolver from the options config.\n *\n * @param options - Server creation options containing permission config\n * @returns A configured PermissionResolver\n */\nfunction buildPermissionResolver(\n options: CreatePermissionBasedMcpServerOptions\n): PermissionResolver {\n const builder = PermissionResolver.builder()\n .source(options.permissions.source)\n .headerName(options.permissions.headerName ?? \"mcp-toolset-permissions\")\n .staticMap(options.permissions.staticMap ?? {})\n .defaultPermissions(options.permissions.defaultPermissions ?? []);\n\n if (options.permissions.resolver) {\n builder.resolver(options.permissions.resolver);\n }\n\n return builder.build();\n}\n\n/**\n * Builds a ServerOrchestrator configured for permission-based operation\n * (STATIC mode, empty toolsets, no meta-tools, no notifier).\n *\n * @param server - The MCP server instance\n * @param options - Server creation options (catalog, moduleLoaders, context)\n * @param policy - Sanitized exposure policy\n * @returns Orchestrator configured for permission-based operation\n */\nfunction buildPermissionOrchestrator(\n server: McpServer,\n options: CreatePermissionBasedMcpServerOptions,\n policy: ExposurePolicy | undefined\n): ServerOrchestrator {\n const builder = ServerOrchestrator.builder()\n .server(server)\n .catalog(options.catalog)\n .moduleLoaders(options.moduleLoaders ?? {})\n .context(options.context)\n .startup({ mode: \"STATIC\", toolsets: [] })\n .registerMetaTools(false);\n\n if (policy) {\n builder.exposurePolicy(policy);\n }\n\n return builder.build();\n}\n\n/**\n * Creates the callback that produces a fresh server + orchestrator per client,\n * scoped to the client's allowed toolsets.\n *\n * @param options - Server creation options\n * @param policy - Sanitized exposure policy\n * @returns Factory callback that accepts allowed toolsets and returns a server/orchestrator pair\n */\nfunction createClientOrchestratorFactory(\n options: CreatePermissionBasedMcpServerOptions,\n policy: ExposurePolicy | undefined\n): (allowedToolsets: string[]) => { server: McpServer; orchestrator: ServerOrchestrator } {\n return (allowedToolsets: string[]) => {\n const clientServer: McpServer = options.createServer();\n const clientOrchestrator = buildPermissionOrchestrator(clientServer, options, policy);\n return { server: clientServer, orchestrator: clientOrchestrator };\n };\n}\n\n/**\n * Builds the PermissionAwareFastifyTransport, handling conditional `.app()`\n * and `.customEndpoints()` chaining.\n *\n * @param options - Server creation options (http config)\n * @param manager - Default DynamicToolManager for status endpoints\n * @param createBundle - Permission-aware bundle creator\n * @returns A configured PermissionAwareFastifyTransport\n */\nfunction buildPermissionTransport(\n options: CreatePermissionBasedMcpServerOptions,\n manager: ReturnType<ServerOrchestrator[\"getManager\"]>,\n createBundle: ReturnType<typeof createPermissionAwareBundle>\n): PermissionAwareFastifyTransport {\n const builder = PermissionAwareFastifyTransport.builder()\n .defaultManager(manager)\n .createPermissionAwareBundle(createBundle)\n .host(options.http?.host ?? \"0.0.0.0\")\n .port(options.http?.port ?? 3000)\n .basePath(options.http?.basePath ?? \"/\")\n .cors(options.http?.cors ?? true)\n .logger(options.http?.logger ?? false);\n\n if (options.http?.app) {\n builder.app(options.http.app);\n }\n if (options.http?.customEndpoints) {\n builder.customEndpoints(options.http.customEndpoints);\n }\n if (options.configSchema) {\n builder.configSchema(options.configSchema);\n }\n\n return builder.build();\n}\n"],"names":["DEFAULT_KEYS","RESERVED_TOOLSET_KEYS","ToolsetValidator","options","__publicField","opts","builder","value","env","args","input","catalog","raw","s","valid","result","name","toolsets","modules","def","m","sanitized","available","toolsetNames","error","source","key","ModuleResolver","context","collected","toolsetName","modKey","loader","loaded","err","ToolingError","message","code","details","_options","ToolRegistry","toolsetKey","toolName","set","tools","t","safe","k","v","DynamicToolManager","skipNotification","validation","policyCheck","registeredTools","toolCount","resolvedTools","mapped","tool","activeToolsets","results","res","successAll","r","anySuccess","all","META_TOOLSET_KEY","registerMetaTools","server","manager","toolRegistry","z","byToolset","items","payload","status","ServerOrchestrator","startup","resolved","managerBuilder","initial","mode","names","isValid","_ClientResourceCache","__privateAdd","_ClientResourceCache_instances","pruneEvery","entry","resource","newEntry","__privateMethod","callEvictCallback_fn","clearEntries","entries","lruKey","now","keysToDelete","ClientResourceCache","defineEndpoint","definition","definePermissionAwareEndpoint","registerCustomEndpoints","app","basePath","endpoints","reservedPaths","endpoint","fullPath","reserved","method","req","reply","clientIdHeader","clientId","randomUUID","body","bodyResult","createValidationError","query","queryResult","params","paramsResult","customRequest","additionalContext","responseResult","field","mcpClientIdSchema","FastifyTransport","defaultManager","createBundle","configSchema","sessionContextResolver","baseContext","_key","bundle","_defaultManager","_createBundle","_configSchema","_sessionContextResolver","_baseContext","Fastify","cors","base","_req","parseResult","cacheKey","mergedContext","created","sessionId","transport","isInitializeRequest","newSessionId","StreamableHTTPServerTransport","sid","sessionRequestContext","headers","rawQuery","SessionContextResolver","config","request","parsedConfig","resolver","rawValue","jsonString","parsed","filtered","sessionConfig","override","baseValue","sortedKeys","normalizedObj","createHash","validateSessionContextConfig","validateConfigExists","validateEnabledField","validateQueryParamConfig","validateContextResolver","validateMergeStrategy","i","startupConfigSchema","validateStartupConfig","formatted","createToolsChangedNotifier","hasNotifierA","hasNotifierB","target","resolveMetaToolsFlag","explicit","createMcpServer","validateOptions","shouldRegisterMetaTools","buildSessionContextResolver","notifyToolsChanged","baseServer","baseOrchestrator","buildOrchestrator","bundleFactory","createBundleFactory","buildTransport","effectiveContext","clientServer","clientOrchestrator","validatePermissionConfig","validateSourceField","validateConfigBasedPermissions","validateTypes","validateStaticMapValues","staticMap","permissions","createPermissionAwareBundle","originalCreateBundle","permissionResolver","requestedToolsets","enabledToolsets","failedToolsets","sanitizeExposurePolicyForPermissions","policy","_PermissionResolver","_PermissionResolver_instances","parseHeaderPermissions_fn","resolveConfigPermissions_fn","headerValue","findHeaderCaseInsensitive_fn","normalizedKey","resolverResult","tryResolverFunction_fn","staticResult","lookupStaticMap_fn","PermissionResolver","_PermissionAwareFastifyTransport","_PermissionAwareFastifyTransport_instances","cleanupBundle_fn","_createPermissionAwareBundle","normalizeBasePath_fn","registerHealthEndpoint_fn","registerToolsEndpoint_fn","registerConfigDiscoveryEndpoint_fn","registerMcpPostEndpoint_fn","registerMcpGetEndpoint_fn","registerMcpDeleteEndpoint_fn","extractClientContext_fn","providedSessions","createSafeErrorResponse_fn","PermissionAwareFastifyTransport","createPermissionBasedMcpServer","validatePermissionOptions","sanitizedPolicy","buildPermissionResolver","buildPermissionOrchestrator","createClientOrchestratorFactory","buildPermissionTransport","allowedToolsets"],"mappings":"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"}