toolception 0.4.0 → 0.5.0

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.
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/FastifyTransport.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"],"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\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 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 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 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 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\";\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 * @param server - The MCP server to register tools on\n * @param manager - The DynamicToolManager instance\n * @param options - Configuration options including the mode\n */\nexport function registerMetaTools(\n server: McpServer,\n manager: DynamicToolManager,\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 server.tool(\n \"enable_toolset\",\n \"Enable a toolset by name\",\n { name: z.string().describe(\"Toolset name\") },\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 server.tool(\n \"disable_toolset\",\n \"Disable a toolset by name (state only)\",\n { name: z.string().describe(\"Toolset name\") },\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 server.tool(\n \"list_toolsets\",\n \"List available toolsets with active status and definitions\",\n {},\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 server.tool(\n \"describe_toolset\",\n \"Describe a toolset with definition, active status and tools\",\n { name: z.string().describe(\"Toolset name\") },\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 server.tool(\n \"list_tools\",\n \"List currently registered tool names (best effort)\",\n {},\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, { 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 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 { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport { isInitializeRequest } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.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\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 };\n private readonly defaultManager: DynamicToolManager;\n private readonly createBundle: () => {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n };\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: () => { server: McpServer; orchestrator: ServerOrchestrator },\n options: FastifyTransportOptions = {},\n configSchema?: object\n ) {\n this.defaultManager = defaultManager;\n this.createBundle = createBundle;\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 };\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 let bundle = useCache ? this.clientCache.get(clientId) : null;\n if (!bundle) {\n const created = this.createBundle();\n const providedSessions = (created as any).sessions;\n bundle = {\n server: created.server,\n orchestrator: created.orchestrator,\n sessions:\n providedSessions instanceof Map ? providedSessions : new Map(),\n };\n if (useCache) this.clientCache.set(clientId, 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 // 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","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type {\n ExposurePolicy,\n Mode,\n ModuleLoader,\n ToolSetCatalog,\n} from \"../types/index.js\";\nimport { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport {\n FastifyTransport,\n type FastifyTransportOptions,\n} from \"../http/FastifyTransport.js\";\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\nexport async function createMcpServer(options: CreateMcpServerOptions) {\n const mode: Exclude<Mode, \"ALL\"> = options.startup?.mode ?? \"DYNAMIC\";\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 * @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 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 const transport = new FastifyTransport(\n orchestrator.getManager(),\n () => {\n // Create a server + orchestrator bundle\n // for a new client when needed\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: options.context,\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 );\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\";\n\nexport interface PermissionAwareFastifyTransportOptions {\n host?: string;\n port?: number;\n basePath?: string;\n cors?: boolean;\n logger?: boolean;\n app?: FastifyInstance;\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 };\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 };\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 // 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 { 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 // 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"],"names":["DEFAULT_KEYS","ToolsetValidator","options","env","args","input","catalog","raw","s","valid","result","name","toolsets","modules","def","m","sanitized","toolsetNames","error","source","key","value","ModuleResolver","context","collected","modKey","loader","loaded","err","ToolingError","message","code","details","_options","ToolRegistry","toolsetKey","toolName","set","tools","safe","k","v","DynamicToolManager","toolsetName","skipNotification","validation","policyCheck","registeredTools","resolvedTools","mapped","tool","activeToolsets","results","res","successAll","r","anySuccess","all","registerMetaTools","server","manager","z","available","byToolset","items","payload","status","ServerOrchestrator","startup","resolved","toolRegistry","initial","names","isValid","ClientResourceCache","__privateAdd","_ClientResourceCache_instances","pruneEvery","entry","resource","newEntry","__privateMethod","callEvictCallback_fn","clearEntries","entries","lruKey","now","keysToDelete","FastifyTransport","defaultManager","createBundle","configSchema","_key","bundle","app","Fastify","cors","base","_req","reply","req","clientIdHeader","clientId","randomUUID","useCache","created","providedSessions","sessionId","transport","isInitializeRequest","newSessionId","StreamableHTTPServerTransport","sid","createMcpServer","mode","baseServer","hasNotifierA","hasNotifierB","notifyToolsChanged","target","orchestrator","createdServer","createdOrchestrator","validatePermissionConfig","config","validateConfigExists","validateSourceField","validateConfigBasedPermissions","validateTypes","validateStaticMapValues","staticMap","permissions","PermissionResolver","_PermissionResolver_instances","headers","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","basePath","extractClientContext_fn","createSafeErrorResponse_fn","sanitizeExposurePolicyForPermissions","policy","createPermissionBasedMcpServer","sanitizedPolicy","baseOrchestrator","allowedToolsets","clientServer","clientOrchestrator"],"mappings":"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"}
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/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\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 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 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 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 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\";\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 * @param server - The MCP server to register tools on\n * @param manager - The DynamicToolManager instance\n * @param options - Configuration options including the mode\n */\nexport function registerMetaTools(\n server: McpServer,\n manager: DynamicToolManager,\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 server.tool(\n \"enable_toolset\",\n \"Enable a toolset by name\",\n { name: z.string().describe(\"Toolset name\") },\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 server.tool(\n \"disable_toolset\",\n \"Disable a toolset by name (state only)\",\n { name: z.string().describe(\"Toolset name\") },\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 server.tool(\n \"list_toolsets\",\n \"List available toolsets with active status and definitions\",\n {},\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 server.tool(\n \"describe_toolset\",\n \"Describe a toolset with definition, active status and tools\",\n { name: z.string().describe(\"Toolset name\") },\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 server.tool(\n \"list_tools\",\n \"List currently registered tool names (best effort)\",\n {},\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, { 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 { 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\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: () => {\n server: McpServer;\n orchestrator: ServerOrchestrator;\n };\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: () => { server: McpServer; orchestrator: ServerOrchestrator },\n options: FastifyTransportOptions = {},\n configSchema?: object\n ) {\n this.defaultManager = defaultManager;\n this.createBundle = createBundle;\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 let bundle = useCache ? this.clientCache.get(clientId) : null;\n if (!bundle) {\n const created = this.createBundle();\n const providedSessions = (created as any).sessions;\n bundle = {\n server: created.server,\n orchestrator: created.orchestrator,\n sessions:\n providedSessions instanceof Map ? providedSessions : new Map(),\n };\n if (useCache) this.clientCache.set(clientId, 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","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type {\n ExposurePolicy,\n Mode,\n ModuleLoader,\n ToolSetCatalog,\n} from \"../types/index.js\";\nimport { ServerOrchestrator } from \"../core/ServerOrchestrator.js\";\nimport {\n FastifyTransport,\n type FastifyTransportOptions,\n} from \"../http/FastifyTransport.js\";\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\nexport async function createMcpServer(options: CreateMcpServerOptions) {\n const mode: Exclude<Mode, \"ALL\"> = options.startup?.mode ?? \"DYNAMIC\";\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 const transport = new FastifyTransport(\n orchestrator.getManager(),\n () => {\n // Create a server + orchestrator bundle\n // for a new client when needed\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: options.context,\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 );\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 { 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 // 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","env","args","input","catalog","raw","s","valid","result","name","toolsets","modules","def","m","sanitized","toolsetNames","error","source","key","value","ModuleResolver","context","collected","modKey","loader","loaded","err","ToolingError","message","code","details","_options","ToolRegistry","toolsetKey","toolName","set","tools","safe","k","v","DynamicToolManager","toolsetName","skipNotification","validation","policyCheck","registeredTools","resolvedTools","mapped","tool","activeToolsets","results","res","successAll","r","anySuccess","all","registerMetaTools","server","manager","z","available","byToolset","items","payload","status","ServerOrchestrator","startup","resolved","toolRegistry","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","_key","bundle","Fastify","cors","base","_req","useCache","created","providedSessions","sessionId","transport","isInitializeRequest","newSessionId","StreamableHTTPServerTransport","sid","createMcpServer","mode","baseServer","hasNotifierA","hasNotifierB","notifyToolsChanged","target","orchestrator","createdServer","createdOrchestrator","validatePermissionConfig","config","validateConfigExists","validateSourceField","validateConfigBasedPermissions","validateTypes","validateStaticMapValues","staticMap","permissions","PermissionResolver","_PermissionResolver_instances","headers","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","createSafeErrorResponse_fn","sanitizeExposurePolicyForPermissions","policy","createPermissionBasedMcpServer","sanitizedPolicy","baseOrchestrator","allowedToolsets","clientServer","clientOrchestrator","defineEndpoint","definition","definePermissionAwareEndpoint"],"mappings":"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"}