Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm
Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)
Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability
Migration completed: $(date)
🤖 Clean migration with full functionality preserved
312 lines
8.1 KiB
JavaScript
312 lines
8.1 KiB
JavaScript
/**
|
|
* 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<any>} 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>} 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<object|null>} 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;
|