/** * tool-bridge.js * Service to execute MCP tools from the UI * MVP1: Auto-injects context from ContextStore, removes hardcoded defaults */ import contextStore from '../stores/context-store.js'; class ToolBridge { constructor() { this.apiBase = '/api'; this.toolCache = new Map(); this.context = null; // Deprecated - use contextStore instead } /** * MVP1: Get execution context from ContextStore * NO DEFAULTS - throws if project not selected * @returns {object} Context with projectId, teamId, userId */ getContext() { const mcpContext = contextStore.getMCPContext(); // Validate required context if (!mcpContext.project_id) { throw new Error('No project selected. Please select a project from the header.'); } return { projectId: mcpContext.project_id, teamId: mcpContext.team_id, userId: mcpContext.user_id, capabilities: mcpContext.capabilities }; } /** * Set execution context (DEPRECATED - use contextStore.setProject/setTeam instead) * @param {string} projectId - Project ID * @param {string} userId - User ID */ setContext(projectId, userId) { console.warn('[ToolBridge] setContext is deprecated. Use contextStore.setProject() instead'); contextStore.setProject(projectId); this.context = { projectId, userId }; } /** * MVP1: Execute an MCP tool with auto-context injection * Implements interceptor pattern with standardized error handling * @param {string} toolName - MCP tool name (e.g., 'dss_extract_tokens') * @param {object} params - Tool parameters * @returns {Promise} Tool execution result */ async executeTool(toolName, params = {}) { console.log(`[ToolBridge] Executing tool: ${toolName}`, params); try { // Auto-inject context from ContextStore const context = this.getContext(); // Wrap parameters with auto-injected context const payload = { project_id: context.projectId, team_id: context.teamId, user_id: context.userId, arguments: params, _client_timestamp: Date.now() // For debugging }; const response = await fetch(`${this.apiBase}/mcp/tools/${toolName}/execute`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); // Standardized error handling if (!response.ok) { // Handle specific HTTP status codes if (response.status === 502) { throw new Error('MCP Server Offline - Please check backend connection'); } if (response.status === 404) { throw new Error(`Tool not found: ${toolName}`); } const errorText = await response.text(); let errorMessage; try { const errorJson = JSON.parse(errorText); errorMessage = errorJson.message || errorJson.detail || `Tool execution failed: ${response.statusText}`; } catch (e) { errorMessage = `Tool execution failed: ${response.status} - ${errorText}`; } throw new Error(errorMessage); } const result = await response.json(); console.log(`[ToolBridge] Tool ${toolName} completed:`, result); return result; } catch (error) { console.error(`[ToolBridge] Tool ${toolName} failed:`, error); // Re-throw with enhanced error message if it's a context error if (error.message.includes('No project selected')) { throw new Error(`${error.message}\n\nTool: ${toolName}`); } throw error; } } /** * Get list of available MCP tools * @returns {Promise} Array of tool definitions */ async getAvailableTools() { if (this.toolCache.has('all')) { return this.toolCache.get('all'); } try { const response = await fetch(`${this.apiBase}/mcp/tools`); if (!response.ok) { throw new Error(`Failed to fetch tools: ${response.statusText}`); } const tools = await response.json(); this.toolCache.set('all', tools); return tools; } catch (error) { console.error('[ToolBridge] Failed to fetch available tools:', error); return []; } } /** * Get tool definition by name * @param {string} toolName - MCP tool name * @returns {Promise} Tool definition */ async getToolDefinition(toolName) { const tools = await this.getAvailableTools(); return tools.find(tool => tool.name === toolName) || null; } /** * Execute dss_extract_tokens tool */ async extractTokens(path, sources = ['css', 'scss', 'tailwind', 'json']) { return this.executeTool('dss_extract_tokens', { path, sources }); } /** * Execute dss_generate_theme tool */ async generateTheme(format, tokens = null, themeName = 'default') { return this.executeTool('dss_generate_theme', { format, tokens, theme_name: themeName }); } /** * Execute dss_sync_figma tool */ async syncFigma(fileKey) { return this.executeTool('dss_sync_figma', { file_key: fileKey }); } /** * Execute dss_resolve_token tool */ async resolveToken(manifestPath, tokenPath, forceRefresh = false) { return this.executeTool('dss_resolve_token', { manifest_path: manifestPath, token_path: tokenPath, force_refresh: forceRefresh }); } /** * Execute dss_get_resolved_context tool */ async getResolvedContext(manifestPath, debug = false, forceRefresh = false) { return this.executeTool('dss_get_resolved_context', { manifest_path: manifestPath, debug, force_refresh: forceRefresh }); } /** * Execute browser_get_logs tool */ async getBrowserLogs(level = 'all', limit = 100) { return this.executeTool('browser_get_logs', { level, limit }); } /** * Execute browser_get_errors tool */ async getBrowserErrors(limit = 50) { return this.executeTool('browser_get_errors', { limit }); } /** * Execute browser_accessibility_audit tool */ async runAccessibilityAudit(selector = null) { return this.executeTool('browser_accessibility_audit', { selector }); } /** * Execute browser_performance tool */ async getPerformanceMetrics() { return this.executeTool('browser_performance'); } /** * Execute devtools_screenshot tool */ async takeScreenshot(fullPage = false, selector = null) { return this.executeTool('devtools_screenshot', { full_page: fullPage, selector }); } /** * Execute devtools_query_dom tool */ async queryDOM(selector) { return this.executeTool('devtools_query_dom', { selector }); } /** * Execute devtools_network_requests tool */ async getNetworkRequests(filterUrl = null, limit = 50) { return this.executeTool('devtools_network_requests', { filter_url: filterUrl, limit }); } /** * Execute dss_audit_components tool */ async auditComponents(path) { return this.executeTool('dss_audit_components', { path }); } /** * Execute dss_find_quick_wins tool */ async findQuickWins(path) { return this.executeTool('dss_find_quick_wins', { path }); } /** * Execute dss_get_status tool */ async getDSSStatus(format = 'json') { return this.executeTool('dss_get_status', { format }); } /** * Execute dss_list_themes tool */ async listThemes() { return this.executeTool('dss_list_themes'); } /** * Execute browser_dom_snapshot tool */ async getDOMSnapshot() { return this.executeTool('browser_dom_snapshot'); } /** * Execute devtools_console_logs tool */ async getConsoleLogs(level = 'all', limit = 100, clear = false) { return this.executeTool('devtools_console_logs', { level, limit, clear }); } /** * Get token information from Context Compiler */ async getTokens(manifestPath) { return this.getResolvedContext(manifestPath, false, false); } /** * Clear browser console logs */ async clearConsoleLogs() { return this.executeTool('devtools_console_logs', { clear: true, limit: 0 }); } } // Singleton instance const toolBridge = new ToolBridge(); export default toolBridge;