Skip to main content
Glama
index.js51.7 kB
#!/usr/bin/env node import { Server } from '@modelcontextprotocol/sdk/server/index.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from '@modelcontextprotocol/sdk/types.js'; import fs from 'fs/promises'; import path from 'path'; import { fileURLToPath } from 'url'; import { getDatabase, closeDatabase } from './src/db.js'; import * as Search from './src/search/index.js'; import * as Validation from './src/validation/index.js'; import * as Generator from './src/generator/index.js'; import * as Relationships from './src/relationships/index.js'; import { performHealthCheck } from './src/utils/health-check.js'; import { globalCache, startCleanupJob } from './src/utils/cache.js'; import { globalTracker } from './src/utils/performance.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); // Load ECL data const eclDataPath = path.join(__dirname, 'ecl-data.json'); let eclData; try { const data = await fs.readFile(eclDataPath, 'utf-8'); eclData = JSON.parse(data); } catch (error) { console.error('Error loading ECL data:', error); process.exit(1); } // Create server instance const server = new Server( { name: 'ecl-mcp-server', version: '1.0.0', }, { capabilities: { resources: {}, tools: {}, }, } ); // List available resources server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: 'ecl://overview', mimeType: 'application/json', name: 'ECL Overview', description: 'Complete overview of Europa Component Library', }, { uri: 'ecl://installation', mimeType: 'application/json', name: 'ECL Installation Guide', description: 'How to install and set up ECL in your project', }, { uri: 'ecl://components', mimeType: 'application/json', name: 'ECL Components List', description: 'List of all available ECL components', }, { uri: 'ecl://guidelines', mimeType: 'application/json', name: 'ECL Guidelines', description: 'Design guidelines for typography, colors, spacing, etc.', }, { uri: 'ecl://setup-template', mimeType: 'text/html', name: 'ECL HTML Setup Template', description: 'Ready-to-use HTML template with ECL integration', }, ], }; }); // Read resource content server.setRequestHandler(ReadResourceRequestSchema, async (request) => { const uri = request.params.uri; if (uri === 'ecl://overview') { return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify( { name: eclData.name, description: eclData.description, version: eclData.version, resources: eclData.resources, important_notes: eclData.important_notes, }, null, 2 ), }, ], }; } if (uri === 'ecl://installation') { return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify( { installation: eclData.installation, setup: eclData.setup, }, null, 2 ), }, ], }; } if (uri === 'ecl://components') { return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify( { components: eclData.components, total_count: Object.keys(eclData.components).length, }, null, 2 ), }, ], }; } if (uri === 'ecl://guidelines') { return { contents: [ { uri, mimeType: 'application/json', text: JSON.stringify( { guidelines: eclData.guidelines, utilities: eclData.utilities, }, null, 2 ), }, ], }; } if (uri === 'ecl://setup-template') { return { contents: [ { uri, mimeType: 'text/html', text: eclData.setup.html_template, }, ], }; } throw new Error(`Unknown resource: ${uri}`); }); // List available tools server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ // ===== LEGACY TOOLS (Phase 1 - for backward compatibility) ===== { name: 'get_component', description: '[LEGACY] Get basic component info from JSON. Use ecl_get_component_details for complete information.', inputSchema: { type: 'object', properties: { component_name: { type: 'string', description: 'Name of the ECL component (e.g., "button", "card", "accordion")', }, }, required: ['component_name'], }, }, { name: 'search_components', description: '[LEGACY] Basic component search. Use ecl_search_components for advanced filtering.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (can be category like "forms", "navigation" or keyword like "input", "menu")', }, }, required: ['query'], }, }, // ===== ENHANCED COMPONENT SEARCH (Phase 3) ===== { name: 'ecl_search_components', description: 'Advanced component search with filters (category, tags, complexity, JS requirements). Returns structured metadata.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (component name, description keyword)', }, category: { type: 'string', description: 'Filter by category (e.g., "forms", "navigation", "content")', }, tag: { type: 'string', description: 'Filter by tag (e.g., "interactive", "form-control", "responsive")', }, complexity: { type: 'string', enum: ['basic', 'intermediate', 'advanced'], description: 'Filter by complexity level', }, requiresJs: { type: 'boolean', description: 'Filter components that require/don\'t require JavaScript', }, limit: { type: 'number', description: 'Max results (default: 20)', }, }, }, }, { name: 'ecl_get_component_details', description: 'Get complete component information including metadata, tags, guidance (do/don\'t), API docs, and code examples.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name or page ID', }, }, required: ['component'], }, }, // ===== API DOCUMENTATION SEARCH (Phase 3) ===== { name: 'ecl_search_api', description: 'Search component API documentation (attributes, props, methods, events, slots, CSS variables).', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (API name, description keyword)', }, apiType: { type: 'string', enum: ['attribute', 'prop', 'method', 'event', 'slot', 'css-variable'], description: 'Filter by API type', }, required: { type: 'boolean', description: 'Filter by required/optional status', }, limit: { type: 'number', description: 'Max results (default: 50)', }, }, }, }, { name: 'ecl_get_component_api', description: 'Get all API documentation for a specific component (all attributes, props, methods, events).', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name or page ID', }, }, required: ['component'], }, }, // ===== CODE EXAMPLE SEARCH (Phase 3) ===== { name: 'ecl_search_code_examples', description: 'Search code examples by component, language, complexity, or completeness.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Filter by component name', }, language: { type: 'string', enum: ['html', 'javascript', 'css', 'jsx', 'vue', 'twig'], description: 'Filter by programming language', }, complexity: { type: 'string', enum: ['basic', 'intermediate', 'advanced'], description: 'Filter by complexity level', }, completeExample: { type: 'boolean', description: 'Filter by complete vs snippet examples', }, interactive: { type: 'boolean', description: 'Filter by interactive examples', }, limit: { type: 'number', description: 'Max results (default: 30)', }, }, }, }, { name: 'ecl_get_example', description: 'Get complete code for a specific example by ID.', inputSchema: { type: 'object', properties: { exampleId: { type: 'number', description: 'Example ID from code_examples table', }, }, required: ['exampleId'], }, }, { name: 'ecl_get_component_examples', description: 'Get all code examples for a specific component.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name or page ID', }, }, required: ['component'], }, }, // ===== USAGE GUIDANCE (Phase 3) ===== { name: 'ecl_get_component_guidance', description: 'Get usage guidance for a component (when to use, when not to use, do\'s, don\'ts, best practices, caveats).', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name or page ID', }, }, required: ['component'], }, }, { name: 'ecl_search_guidance', description: 'Search guidance content across all components by keyword.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (guidance content keyword)', }, guidanceType: { type: 'string', enum: ['when-to-use', 'when-not-to-use', 'do', 'dont', 'best-practice', 'caveat', 'limitation', 'note'], description: 'Filter by guidance type', }, limit: { type: 'number', description: 'Max results (default: 30)', }, }, }, }, // ===== COMPONENT RELATIONSHIPS (Phase 3) ===== { name: 'ecl_find_related_components', description: 'Find components related to a given component (requires, suggests, alternatives, contains, conflicts).', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name or page ID', }, relationshipType: { type: 'string', enum: ['requires', 'suggests', 'alternative', 'contains', 'conflicts', 'extends'], description: 'Filter by relationship type', }, }, required: ['component'], }, }, { name: 'ecl_get_dependency_graph', description: 'Build recursive dependency graph for a component (what it requires and what requires it).', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name or page ID', }, maxDepth: { type: 'number', description: 'Maximum recursion depth (default: 3)', }, }, required: ['component'], }, }, // ===== DESIGN TOKENS (Phase 3) ===== { name: 'ecl_search_design_tokens', description: 'Search design tokens (colors, spacing, typography, breakpoints, etc.) by name or category.', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query (token name, CSS variable, or value)', }, category: { type: 'string', enum: ['color', 'spacing', 'typography', 'breakpoint', 'shadow', 'border-radius', 'z-index', 'timing'], description: 'Filter by token category', }, limit: { type: 'number', description: 'Max results (default: 50)', }, }, }, }, { name: 'ecl_get_tokens_by_category', description: 'Get all design tokens for a specific category.', inputSchema: { type: 'object', properties: { category: { type: 'string', enum: ['color', 'spacing', 'typography', 'breakpoint', 'shadow', 'border-radius', 'z-index', 'timing'], description: 'Token category', }, }, required: ['category'], }, }, { name: 'ecl_get_token', description: 'Get a specific design token by exact name or CSS variable.', inputSchema: { type: 'object', properties: { tokenName: { type: 'string', description: 'Token name or CSS variable (e.g., "primary-color" or "--ecl-color-primary")', }, }, required: ['tokenName'], }, }, { name: 'ecl_get_token_categories', description: 'List all design token categories with counts.', inputSchema: { type: 'object', properties: {}, }, }, { name: 'generate_component_code', description: 'Generate ready-to-use HTML code for an ECL component with optional customization', inputSchema: { type: 'object', properties: { component_name: { type: 'string', description: 'Name of the ECL component', }, variant: { type: 'string', description: 'Optional variant (e.g., "primary", "secondary" for buttons)', }, }, required: ['component_name'], }, }, { name: 'get_setup_guide', description: 'Get complete setup guide for integrating ECL into a project', inputSchema: { type: 'object', properties: { method: { type: 'string', enum: ['npm', 'cdn', 'complete'], description: 'Installation method: npm, cdn, or complete guide', }, }, required: ['method'], }, }, { name: 'list_components_by_category', description: 'List all components in a specific category', inputSchema: { type: 'object', properties: { category: { type: 'string', enum: ['forms', 'navigation', 'content', 'media', 'banners', 'site-wide'], description: 'Component category', }, }, required: ['category'], }, }, { name: 'get_guidelines', description: 'Get ECL design guidelines for typography, colors, spacing, etc.', inputSchema: { type: 'object', properties: { guideline_type: { type: 'string', enum: ['typography', 'colours', 'colors', 'images', 'iconography', 'spacing', 'all'], description: 'Type of guideline to retrieve', }, }, required: ['guideline_type'], }, }, { name: 'get_javascript_init', description: 'Get JavaScript initialization code for ECL components', inputSchema: { type: 'object', properties: { component_name: { type: 'string', description: 'Component name to get initialization code for (optional - returns general info if not provided)', }, }, }, }, // ===== VALIDATION & DIAGNOSTICS (Phase 4) ===== { name: 'ecl_validate_component_usage', description: 'Validate ECL component HTML/JS code against requirements. Checks structure, attributes, accessibility, best practices, and returns quality score.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name to validate', }, html_code: { type: 'string', description: 'HTML code to validate', }, js_code: { type: 'string', description: 'Optional JavaScript code to validate', }, context: { type: 'string', description: 'Optional usage context for contextual validation', }, }, required: ['component', 'html_code'], }, }, { name: 'ecl_check_accessibility', description: 'Check HTML code for WCAG 2.1 accessibility compliance (Level A, AA, AAA). Validates ARIA, keyboard access, contrast, focus, and component-specific requirements.', inputSchema: { type: 'object', properties: { html_code: { type: 'string', description: 'HTML code to check for accessibility', }, component: { type: 'string', description: 'Optional component name for component-specific checks', }, wcag_level: { type: 'string', enum: ['A', 'AA', 'AAA'], description: 'Target WCAG level (default: AA)', }, }, required: ['html_code'], }, }, { name: 'ecl_analyze_ecl_code', description: 'Analyze ECL code for components, design tokens, best practices, and maintainability. Returns quality score and recommendations.', inputSchema: { type: 'object', properties: { html_code: { type: 'string', description: 'HTML code to analyze', }, js_code: { type: 'string', description: 'Optional JavaScript code to analyze', }, css_code: { type: 'string', description: 'Optional CSS code to analyze', }, }, required: ['html_code'], }, }, { name: 'ecl_check_conflicts', description: 'Check for conflicts or compatibility issues between ECL components. Identifies z-index conflicts, nesting issues, and incompatible combinations.', inputSchema: { type: 'object', properties: { components: { type: 'array', items: { type: 'string', }, description: 'Array of component names to check for conflicts', }, context: { type: 'string', description: 'Optional usage context', }, }, required: ['components'], }, }, // ===== CODE GENERATION (Phase 5) ===== { name: 'ecl_get_complete_example', description: 'Get a complete, runnable example for a component with all dependencies (HTML/CSS/JS). Returns full working code with CDN links, initialization scripts, and customization guide.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name', }, example_type: { type: 'string', description: 'Type of example (basic/advanced/interactive)', }, variant: { type: 'string', description: 'Component variant (e.g., primary/secondary)', }, }, required: ['component'], }, }, { name: 'ecl_generate_component', description: 'Generate customized ECL component code with options. Applies variants, sizes, content, attributes, and ensures accessibility compliance.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name to generate', }, customization: { type: 'object', description: 'Customization options (variant, size, color, content, attributes)', }, framework: { type: 'string', enum: ['vanilla', 'react', 'vue'], description: 'Target framework (default: vanilla)', }, include_comments: { type: 'boolean', description: 'Include explanatory comments in generated code', }, }, required: ['component'], }, }, { name: 'ecl_create_playground', description: 'Create a standalone HTML playground file with multiple ECL components for testing. Returns complete HTML file with navigation, examples, and code viewers.', inputSchema: { type: 'object', properties: { components: { type: 'array', items: { type: 'string', }, description: 'Array of component names to include in playground', }, custom_code: { type: 'string', description: 'Optional custom HTML code to include', }, include_all_variants: { type: 'boolean', description: 'Include all variants of each component (default: false)', }, }, required: ['components'], }, }, // ===== RELATIONSHIPS & DEPENDENCIES (Phase 6) ===== { name: 'ecl_find_components_by_tag', description: 'Find components by tags (feature, category, accessibility, interaction). Supports searching with multiple tags and different match modes.', inputSchema: { type: 'object', properties: { tags: { oneOf: [ { type: 'string' }, { type: 'array', items: { type: 'string' } } ], description: 'Single tag or array of tags to search for', }, tag_type: { type: 'string', description: 'Filter by tag type: feature, category, accessibility, interaction', }, match_mode: { type: 'string', description: 'Match mode: "any" (default) or "all" for multiple tags', }, include_metadata: { type: 'boolean', description: 'Include component metadata (default: true)', }, }, required: ['tags'], }, }, { name: 'ecl_get_available_tags', description: 'Get all available tags for categorizing and discovering components. Returns tags grouped by type with usage counts.', inputSchema: { type: 'object', properties: { tag_type: { type: 'string', description: 'Filter by tag type: feature, category, accessibility, interaction', }, include_counts: { type: 'boolean', description: 'Include component counts per tag (default: true)', }, }, }, }, { name: 'ecl_find_similar_components', description: 'Find components similar to a given component based on shared tags. Returns similarity scores and shared features.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name to find similar components for', }, min_shared_tags: { type: 'number', description: 'Minimum shared tags required (default: 2)', }, limit: { type: 'number', description: 'Maximum results to return (default: 10)', }, }, required: ['component'], }, }, { name: 'ecl_analyze_dependencies', description: 'Analyze component dependencies including required ECL assets, JavaScript needs, other components, and accessibility requirements.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name to analyze', }, include_suggestions: { type: 'boolean', description: 'Include suggested components (default: true)', }, include_conflicts: { type: 'boolean', description: 'Include conflicting components (default: true)', }, recursive: { type: 'boolean', description: 'Follow full dependency chain (default: false)', }, }, required: ['component'], }, }, { name: 'ecl_build_relationship_graph', description: 'Build a visualizable graph of component relationships. Returns graph data in Cytoscape, D3, or Mermaid format.', inputSchema: { type: 'object', properties: { components: { type: 'array', items: { type: 'string', }, description: 'Specific components to include (null = all)', }, relationship_types: { type: 'array', items: { type: 'string', }, description: 'Types to include: requires, suggests, contains, alternative', }, max_depth: { type: 'number', description: 'Maximum relationship depth (default: 2)', }, format: { type: 'string', description: 'Output format: cytoscape (default), d3, mermaid', }, }, }, }, { name: 'ecl_analyze_conflicts', description: 'Analyze potential conflicts between multiple components. Returns conflicts, warnings, risk scores, and recommendations.', inputSchema: { type: 'object', properties: { components: { type: 'array', items: { type: 'string', }, description: 'Array of component names to check for conflicts (minimum 2)', }, include_warnings: { type: 'boolean', description: 'Include warning-level conflicts (default: true)', }, include_recommendations: { type: 'boolean', description: 'Include recommendations (default: true)', }, }, required: ['components'], }, }, { name: 'ecl_suggest_alternatives', description: 'Suggest alternative components based on feature similarity. Useful when a component conflicts or doesn\'t fit requirements.', inputSchema: { type: 'object', properties: { component: { type: 'string', description: 'Component name to find alternatives for', }, }, required: ['component'], }, }, // ===== SYSTEM HEALTH & DIAGNOSTICS (Phase 8) ===== { name: 'ecl_health_check', description: 'Get system health status including database, cache, performance metrics, and tool availability.', inputSchema: { type: 'object', properties: {}, }, }, ], }; }); // Handle tool calls server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; // Database connection for Phase 3 tools let db = null; try { // Initialize database for Phase 3 tools if (name.startsWith('ecl_')) { db = getDatabase(true); // readonly } if (name === 'get_component') { const componentName = args.component_name.toLowerCase().replace(/\s+/g, '-'); const component = eclData.components[componentName]; if (!component) { return { content: [ { type: 'text', text: `Component "${args.component_name}" not found. Use search_components to find available components.`, }, ], }; } return { content: [ { type: 'text', text: JSON.stringify( { name: component.name, category: component.category, description: component.description, usage: component.usage, url: component.url, variants: component.variants, dependencies: component.dependencies, auto_init: component.auto_init, example: component.example, }, null, 2 ), }, ], }; } if (name === 'search_components') { const query = args.query.toLowerCase(); const results = []; for (const [key, component] of Object.entries(eclData.components)) { if ( key.includes(query) || component.name.toLowerCase().includes(query) || component.category.includes(query) || component.description.toLowerCase().includes(query) ) { results.push({ id: key, name: component.name, category: component.category, description: component.description, url: component.url, }); } } return { content: [ { type: 'text', text: JSON.stringify( { query: args.query, count: results.length, results: results, }, null, 2 ), }, ], }; } if (name === 'generate_component_code') { const componentName = args.component_name.toLowerCase().replace(/\s+/g, '-'); const component = eclData.components[componentName]; if (!component) { return { content: [ { type: 'text', text: `Component "${args.component_name}" not found.`, }, ], }; } let code = component.example || `<!-- ${component.name} component -->\n<!-- See documentation: ${component.url} -->`; if (args.variant && component.variants) { code += `\n\n<!-- Available variants: ${component.variants.join(', ')} -->`; } if (component.auto_init) { code += `\n\n<!-- Auto-initialization: Add data-ecl-auto-init="${component.auto_init}" to the root element -->`; } return { content: [ { type: 'text', text: code, }, ], }; } if (name === 'get_setup_guide') { const method = args.method; if (method === 'npm') { return { content: [ { type: 'text', text: `# ECL Setup with NPM ## Installation \`\`\`bash ${eclData.installation.npm} # or ${eclData.installation.yarn} \`\`\` ## Setup 1. Install the package in your project 2. Import or copy the CSS and JS files to your public directory 3. Reference them in your HTML: \`\`\`html <link rel="stylesheet" href="/styles/ecl-eu.css" media="screen" /> <script src="/scripts/ecl-eu.js"></script> <script> ECL.autoInit(); </script> \`\`\` ## Optional Dependencies ${eclData.setup.dependencies.pikaday}: ${eclData.setup.dependencies.moment} `, }, ], }; } if (method === 'cdn') { return { content: [ { type: 'text', text: `# ECL Setup with CDN ## CDN Pattern ${eclData.installation.cdn_pattern} ## Example ${eclData.installation.cdn_example} ## Important Notes ${eclData.important_notes.filter((note) => note.includes('CDN') || note.includes('SVG')).join('\n')} ## Setup \`\`\`html <link rel="stylesheet" href="https://cdn1.fpfis.tech.ec.europa.eu/ecl/v${eclData.version}/eu/styles/ecl-eu.css" media="screen" /> <script src="https://cdn1.fpfis.tech.ec.europa.eu/ecl/v${eclData.version}/eu/scripts/ecl-eu.js"></script> <script> ECL.autoInit(); </script> \`\`\` `, }, ], }; } // complete return { content: [ { type: 'text', text: `# Complete ECL Setup Guide ## Installation Options ### NPM (Recommended) \`\`\`bash ${eclData.installation.npm} \`\`\` ### CDN ${eclData.installation.cdn_pattern} ## Complete HTML Template \`\`\`html ${eclData.setup.html_template} \`\`\` ## Optional Styles ${eclData.setup.optional_styles.map((s) => `- **${s.name}**: ${s.description}`).join('\n')} ## JavaScript Initialization - **Auto-init**: ${eclData.setup.javascript.auto_init} - **Data attribute**: ${eclData.setup.javascript.data_attribute} ## Important Notes ${eclData.important_notes.map((note) => `- ${note}`).join('\n')} ## Resources - Playground: ${eclData.resources.playground} - GitHub: ${eclData.resources.github} - NPM: ${eclData.resources.npm_org} `, }, ], }; } if (name === 'list_components_by_category') { const category = args.category; const components = Object.entries(eclData.components) .filter(([_, comp]) => comp.category === category) .map(([key, comp]) => ({ id: key, name: comp.name, description: comp.description, url: comp.url, })); return { content: [ { type: 'text', text: JSON.stringify( { category: category, count: components.length, components: components, }, null, 2 ), }, ], }; } if (name === 'get_guidelines') { const type = args.guideline_type.toLowerCase(); if (type === 'all') { return { content: [ { type: 'text', text: JSON.stringify( { guidelines: eclData.guidelines, utilities: eclData.utilities, }, null, 2 ), }, ], }; } // Handle both 'colours' and 'colors' const guidelineKey = type === 'colors' ? 'colours' : type; const guideline = eclData.guidelines[guidelineKey]; if (!guideline) { return { content: [ { type: 'text', text: `Guideline type "${args.guideline_type}" not found. Available: typography, colours, images, iconography, spacing`, }, ], }; } return { content: [ { type: 'text', text: JSON.stringify(guideline, null, 2), }, ], }; } if (name === 'get_javascript_init') { const componentName = args.component_name ? args.component_name.toLowerCase().replace(/\s+/g, '-') : null; if (componentName) { const component = eclData.components[componentName]; if (!component) { return { content: [ { type: 'text', text: `Component "${args.component_name}" not found.`, }, ], }; } if (!component.auto_init) { return { content: [ { type: 'text', text: `Component "${component.name}" does not require JavaScript initialization.`, }, ], }; } return { content: [ { type: 'text', text: `# JavaScript Initialization for ${component.name} ## Auto-initialization Add the data attribute to the root element: \`\`\`html data-ecl-auto-init="${component.auto_init}" \`\`\` ## Complete Example \`\`\`html ${component.example || `<!-- Add data-ecl-auto-init="${component.auto_init}" to the component root -->`} \`\`\` ## Global Initialization Add to the end of your page: \`\`\`html <script src="/scripts/ecl-eu.js"></script> <script> ECL.autoInit(); </script> \`\`\` For manual initialization, see: ${component.url} `, }, ], }; } // General JavaScript info return { content: [ { type: 'text', text: `# ECL JavaScript Initialization ## Auto-initialization (Recommended) 1. Add data attribute to component root element: \`\`\`html data-ecl-auto-init="[ComponentName]" \`\`\` 2. Call ECL.autoInit() after page load: \`\`\`html <script src="/scripts/ecl-eu.js"></script> <script> ECL.autoInit(); </script> \`\`\` ## Components requiring JavaScript ${Object.entries(eclData.components) .filter(([_, comp]) => comp.auto_init) .map(([_, comp]) => `- **${comp.name}**: data-ecl-auto-init="${comp.auto_init}"`) .join('\n')} ## Dependencies ${Object.entries(eclData.setup.dependencies) .map(([key, value]) => `- **${key}**: ${value}`) .join('\n')} For manual initialization details, check individual component documentation. `, }, ], }; } // ===== PHASE 3 ENHANCED SEARCH TOOLS ===== // Component Search if (name === 'ecl_search_components') { const result = Search.searchComponents(db, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_component_details') { const result = Search.getComponentDetails(db, args.component); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // API Search if (name === 'ecl_search_api') { const result = Search.searchAPI(db, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_component_api') { const result = Search.getComponentAPI(db, args.component); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // Code Example Search if (name === 'ecl_search_code_examples') { const result = Search.searchExamples(db, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_example') { const result = Search.getExample(db, args.exampleId); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_component_examples') { const result = Search.getComponentExamples(db, args.component); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // Guidance Search if (name === 'ecl_get_component_guidance') { const result = Search.getComponentGuidance(db, args.component); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_search_guidance') { const result = Search.searchGuidance(db, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // Relationship Search if (name === 'ecl_find_related_components') { const result = Search.findRelatedComponents(db, args.component, args.relationshipType); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_dependency_graph') { const result = Search.getDependencyGraph(db, args.component, args.maxDepth); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // Design Token Search if (name === 'ecl_search_design_tokens') { const result = Search.searchDesignTokens(db, args); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_tokens_by_category') { const result = Search.getTokensByCategory(db, args.category); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_token') { const result = Search.getToken(db, args.tokenName); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_token_categories') { const result = Search.getTokenCategories(db); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // Validation & Diagnostics if (name === 'ecl_validate_component_usage') { const result = await Validation.validateComponentUsage( db, args.component, args.html_code, args.js_code, args.context ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_check_accessibility') { const result = await Validation.checkAccessibility( db, args.html_code, args.component, args.wcag_level ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_analyze_ecl_code') { const result = await Validation.analyzeEclCode( db, args.html_code, args.js_code, args.css_code ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_check_conflicts') { const result = await Validation.checkConflicts( db, args.components, args.context ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // Code Generation (Phase 5) if (name === 'ecl_get_complete_example') { const result = Generator.getCompleteExample( db, args.component, { exampleType: args.example_type, variant: args.variant } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_generate_component') { const result = Generator.generateComponent( db, args.component, { customization: args.customization, framework: args.framework || 'vanilla', includeComments: args.include_comments || false } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_create_playground') { const result = Generator.createPlayground( db, args.components, { customCode: args.custom_code, includeAllVariants: args.include_all_variants || false } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } // Relationships & Dependencies (Phase 6) if (name === 'ecl_find_components_by_tag') { const result = Relationships.findComponentsByTag( db, args.tags, { tag_type: args.tag_type, match_mode: args.match_mode || 'any', include_metadata: args.include_metadata !== false } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_get_available_tags') { const result = Relationships.getAvailableTags( db, { tag_type: args.tag_type, include_counts: args.include_counts !== false } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_find_similar_components') { const result = Relationships.findSimilarComponents( db, args.component, { min_shared_tags: args.min_shared_tags || 2, limit: args.limit || 10 } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_analyze_dependencies') { const result = Relationships.analyzeComponentDependencies( db, args.component, { include_suggestions: args.include_suggestions !== false, include_conflicts: args.include_conflicts !== false, recursive: args.recursive || false } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_build_relationship_graph') { const result = Relationships.buildRelationshipGraph( db, { components: args.components, relationship_types: args.relationship_types || ['requires', 'suggests', 'contains', 'alternative'], max_depth: args.max_depth || 2, format: args.format || 'cytoscape' } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_analyze_conflicts') { const result = Relationships.analyzeComponentConflicts( db, args.components, { include_warnings: args.include_warnings !== false, include_recommendations: args.include_recommendations !== false } ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_suggest_alternatives') { const result = Relationships.suggestAlternatives( db, args.component ); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } if (name === 'ecl_health_check') { const startTime = Date.now(); const health = performHealthCheck(db); const executionTime = Date.now() - startTime; // Track performance globalTracker.track('ecl_health_check', executionTime, true); return { content: [ { type: 'text', text: JSON.stringify({ success: health.status === 'healthy', data: health, metadata: { tool: 'ecl_health_check', execution_time_ms: executionTime, source: 'system', version: '2.0' } }, null, 2), }, ], }; } return { content: [ { type: 'text', text: `Unknown tool: ${name}`, }, ], }; } catch (error) { return { content: [ { type: 'text', text: `Error: ${error.message}`, }, ], isError: true, }; } finally { // Clean up database connection if (db) { closeDatabase(db); } } }); // Start the server async function main() { const transport = new StdioServerTransport(); await server.connect(transport); // Start cache cleanup job (runs every 5 minutes) startCleanupJob(); console.error('ECL MCP Server running on stdio'); console.error('Cache cleanup job started'); } main().catch((error) => { console.error('Server error:', error); process.exit(1); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/brownrl/eco_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server