UNPKG

@kya-os/mcp-i

Version:

The TypeScript MCP framework with identity features built-in

193 lines (192 loc) 6.86 kB
"use strict"; /** * Tool Protection Configuration and Resolution System (Phase 1.5) * * Enables zero-code tool protection by configuring which tools require delegation * through configuration rather than manual code wrapping. * * Configuration Resolution Priority (highest to lowest): * 1. Inline config (passed to runtime) - for testing and overrides * 2. Local file (tool-protections.json) - for development * 3. AgentShield API - for production (NOT IMPLEMENTED YET) */ Object.defineProperty(exports, "__esModule", { value: true }); exports.ToolProtectionResolver = exports.AgentShieldToolProtectionSource = exports.FileToolProtectionSource = exports.InlineToolProtectionSource = void 0; exports.createToolProtectionResolver = createToolProtectionResolver; /** * Inline configuration source (highest priority) */ class InlineToolProtectionSource { config; name = 'inline'; priority = 100; constructor(config) { this.config = config; } async load() { return this.config; } } exports.InlineToolProtectionSource = InlineToolProtectionSource; /** * Local file configuration source */ class FileToolProtectionSource { filePath; name = 'file'; priority = 50; constructor(filePath) { this.filePath = filePath; } async load() { try { // Dynamic import to avoid bundling issues const fs = await import('fs/promises'); const path = await import('path'); const fullPath = path.resolve(process.cwd(), this.filePath); const content = await fs.readFile(fullPath, 'utf-8'); const config = JSON.parse(content); // Validate structure if (typeof config !== 'object' || config === null) { console.warn(`[ToolProtection] Invalid config file at ${fullPath}: not an object`); return null; } return config; } catch (error) { // File not found is OK - just means no local config if (error.code === 'ENOENT') { return null; } console.warn(`[ToolProtection] Error loading config from ${this.filePath}:`, error); return null; } } } exports.FileToolProtectionSource = FileToolProtectionSource; /** * AgentShield API configuration source (lowest priority) * NOTE: This is a placeholder for future implementation */ class AgentShieldToolProtectionSource { apiUrl; agentDid; apiKey; name = 'agentshield'; priority = 10; constructor(apiUrl, agentDid, apiKey) { this.apiUrl = apiUrl; this.agentDid = agentDid; this.apiKey = apiKey; } async load() { // TODO: Implement AgentShield API fetch // GET /v1/agents/{agentDid}/tool-protections // Headers: { Authorization: `Bearer ${apiKey}` } console.warn('[ToolProtection] AgentShield API source not yet implemented'); return null; } } exports.AgentShieldToolProtectionSource = AgentShieldToolProtectionSource; /** * Tool protection resolver - merges configs from multiple sources */ class ToolProtectionResolver { sources = []; mergedConfig = null; debug; constructor(options = {}) { this.debug = options.debug || false; } /** * Add a configuration source */ addSource(source) { this.sources.push(source); // Sort by priority (highest first) this.sources.sort((a, b) => b.priority - a.priority); } /** * Load and merge all configurations */ async resolve() { if (this.mergedConfig) { return this.mergedConfig; } const configs = []; // Load from all sources in priority order for (const source of this.sources) { if (this.debug) { console.error(`[ToolProtection] Loading config from source: ${source.name} (priority: ${source.priority})`); } const config = await source.load(); if (config) { configs.push({ source: source.name, config }); if (this.debug) { console.error(`[ToolProtection] Loaded ${Object.keys(config).length} tool configs from ${source.name}`); } } } // Merge configs (higher priority sources override lower priority) // Start with lowest priority and overlay higher priority configs this.mergedConfig = {}; for (let i = configs.length - 1; i >= 0; i--) { const { source, config } = configs[i]; for (const [toolName, toolConfig] of Object.entries(config)) { if (this.debug && this.mergedConfig[toolName]) { console.error(`[ToolProtection] Tool "${toolName}" config from ${source} overrides previous config`); } this.mergedConfig[toolName] = toolConfig; } } if (this.debug) { console.error(`[ToolProtection] Final merged config has ${Object.keys(this.mergedConfig).length} protected tools`); console.error('[ToolProtection] Protected tools:', Object.keys(this.mergedConfig)); } return this.mergedConfig; } /** * Get protection config for a specific tool */ getToolProtection(toolName) { if (!this.mergedConfig) { throw new Error('ToolProtectionResolver not initialized - call resolve() first'); } return this.mergedConfig[toolName] || null; } /** * Check if a tool requires delegation */ requiresDelegation(toolName) { const config = this.getToolProtection(toolName); return config?.requiresDelegation || false; } /** * Get required scopes for a tool */ getRequiredScopes(toolName) { const config = this.getToolProtection(toolName); return config?.requiredScopes || []; } } exports.ToolProtectionResolver = ToolProtectionResolver; /** * Create default tool protection resolver */ function createToolProtectionResolver(options) { const resolver = new ToolProtectionResolver({ debug: options.debug }); // Add inline source if provided if (options.inline) { resolver.addSource(new InlineToolProtectionSource(options.inline)); } // Add local file source unless explicitly disabled if (options.localFile !== false) { const filePath = options.localFile || 'tool-protections.json'; resolver.addSource(new FileToolProtectionSource(filePath)); } // Add AgentShield source if configured if (options.agentShield) { resolver.addSource(new AgentShieldToolProtectionSource(options.agentShield.apiUrl, options.agentShield.agentDid, options.agentShield.apiKey)); } return resolver; }