Skip to main content
Glama

Mesh Design System MCP Server

by lukeylias
main.py24.7 kB
#!/usr/bin/env python3 """ MCP Server for Mesh Design System Provides AI assistants access to Mesh components and design tokens """ from fastapi import FastAPI, HTTPException from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from pydantic import BaseModel from typing import Dict, List, Any, Optional import logging import asyncio from datetime import datetime, timedelta from scrapers.mesh_scraper import MeshScraper from cache.cache_manager import CacheManager from generators.data_generator import DataGenerator from models.mcp_models import ( MCPManifest, MCPTool, MCPInvokeRequest, MCPInvokeResponse, PlaceholderDataRequest, ComponentSearchRequest, PrototypeCodeRequest ) # Configure logging logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) # Initialize FastAPI app app = FastAPI( title="Mesh Design System MCP Server", description="Model Context Protocol server for AI assistant integration with Mesh Design System", version="1.0.0" ) # Add CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], # Configure appropriately for production allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Initialize services mesh_scraper = MeshScraper() cache_manager = CacheManager() data_generator = DataGenerator() # MCP Tools Definition MCP_TOOLS = [ MCPTool( name="listComponents", description="Provides a comprehensive list of all available UI components in the Mesh Design System", inputSchema={ "type": "object", "properties": {}, "required": [] } ), MCPTool( name="getComponentDetails", description="Fetches detailed information for a specific component", inputSchema={ "type": "object", "properties": { "componentName": { "type": "string", "description": "Name of the component to get details for" } }, "required": ["componentName"] } ), MCPTool( name="getDesignTokens", description="Provides core design tokens (colors, typography, spacing)", inputSchema={ "type": "object", "properties": { "tokenType": { "type": "string", "description": "Type of tokens to retrieve (optional)", "enum": ["colors", "typography", "spacing", "all"] } }, "required": [] } ), MCPTool( name="generatePlaceholderData", description="Generate realistic placeholder data for insurance/healthcare prototyping", inputSchema={ "type": "object", "properties": { "dataType": { "type": "string", "description": "Type of data to generate", "enum": ["members", "policies", "claims", "providers"] }, "count": { "type": "integer", "description": "Number of records to generate", "default": 10, "minimum": 1, "maximum": 100 } }, "required": ["dataType"] } ), MCPTool( name="searchComponentsByUseCase", description="Find relevant Mesh components for specific UI patterns and use cases", inputSchema={ "type": "object", "properties": { "useCase": { "type": "string", "description": "Description of the UI pattern or use case" } }, "required": ["useCase"] } ), MCPTool( name="generatePrototypeCode", description="Generate complete React component code using Mesh components", inputSchema={ "type": "object", "properties": { "description": { "type": "string", "description": "Description of the component to generate" }, "components": { "type": "array", "items": {"type": "string"}, "description": "Specific Mesh components to include" }, "includeData": { "type": "boolean", "description": "Whether to include placeholder data", "default": True } }, "required": ["description"] } ) ] @app.get("/") async def root(): """Health check endpoint""" return {"message": "Mesh Design System MCP Server", "status": "running", "version": "1.0.0"} @app.get("/tools") async def get_tools() -> MCPManifest: """Returns the MCP manifest with available tools""" logger.info("Returning MCP tools manifest") return MCPManifest(tools=MCP_TOOLS) @app.post("/tools/{tool_name}/invoke") async def invoke_tool(tool_name: str, request: MCPInvokeRequest) -> MCPInvokeResponse: """Execute a specific MCP tool""" logger.info(f"Invoking tool: {tool_name} with args: {request.arguments}") try: if tool_name == "listComponents": result = await handle_list_components() elif tool_name == "getComponentDetails": component_name = request.arguments.get("componentName") if not component_name: raise HTTPException(status_code=400, detail="componentName is required") result = await handle_get_component_details(component_name) elif tool_name == "getDesignTokens": token_type = request.arguments.get("tokenType", "all") result = await handle_get_design_tokens(token_type) elif tool_name == "generatePlaceholderData": data_type = request.arguments.get("dataType") count = request.arguments.get("count", 10) if not data_type: raise HTTPException(status_code=400, detail="dataType is required") result = await handle_generate_placeholder_data(data_type, count) elif tool_name == "searchComponentsByUseCase": use_case = request.arguments.get("useCase") if not use_case: raise HTTPException(status_code=400, detail="useCase is required") result = await handle_search_components_by_use_case(use_case) elif tool_name == "generatePrototypeCode": description = request.arguments.get("description") components = request.arguments.get("components", []) include_data = request.arguments.get("includeData", True) if not description: raise HTTPException(status_code=400, detail="description is required") result = await handle_generate_prototype_code(description, components, include_data) else: raise HTTPException(status_code=404, detail=f"Tool {tool_name} not found") return MCPInvokeResponse(content=[{"type": "text", "text": str(result)}]) except Exception as e: logger.error(f"Error invoking tool {tool_name}: {str(e)}") raise HTTPException(status_code=500, detail=f"Tool execution failed: {str(e)}") async def handle_list_components() -> List[str]: """Handle listComponents tool execution""" cache_key = "mesh_components_list" # Check cache first cached_result = await cache_manager.get(cache_key) if cached_result: logger.info("Returning cached components list") return cached_result # Scrape components if not cached logger.info("Scraping components list from Mesh Design System") components = await mesh_scraper.scrape_components_list() # Cache the result await cache_manager.set(cache_key, components, ttl=3600) # 1 hour TTL return components async def handle_get_component_details(component_name: str) -> Dict[str, Any]: """Handle getComponentDetails tool execution""" cache_key = f"mesh_component_{component_name.lower()}" # Check cache first cached_result = await cache_manager.get(cache_key) if cached_result: logger.info(f"Returning cached details for component: {component_name}") return cached_result # Scrape component details if not cached logger.info(f"Scraping details for component: {component_name}") details = await mesh_scraper.scrape_component_details(component_name) if not details: raise HTTPException(status_code=404, detail=f"Component '{component_name}' not found") # Cache the result await cache_manager.set(cache_key, details, ttl=7200) # 2 hours TTL return details async def handle_get_design_tokens(token_type: str = "all") -> Dict[str, Any]: """Handle getDesignTokens tool execution""" cache_key = f"mesh_design_tokens_{token_type}" # Check cache first cached_result = await cache_manager.get(cache_key) if cached_result: logger.info(f"Returning cached design tokens for type: {token_type}") return cached_result # Scrape design tokens if not cached logger.info(f"Scraping design tokens for type: {token_type}") tokens = await mesh_scraper.scrape_design_tokens(token_type) # Cache the result await cache_manager.set(cache_key, tokens, ttl=7200) # 2 hours TTL return tokens async def handle_generate_placeholder_data(data_type: str, count: int = 10) -> List[Dict[str, Any]]: """Handle generatePlaceholderData tool execution""" cache_key = f"placeholder_data_{data_type}_{count}" # Check cache first (shorter TTL for placeholder data) cached_result = await cache_manager.get(cache_key) if cached_result: logger.info(f"Returning cached placeholder data for type: {data_type}, count: {count}") return cached_result # Generate new data logger.info(f"Generating placeholder data for type: {data_type}, count: {count}") try: data = data_generator.generate_data(data_type, count) # Cache the result with shorter TTL (30 minutes) await cache_manager.set(cache_key, data, ttl=1800) return data except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) except Exception as e: logger.error(f"Error generating placeholder data: {str(e)}") raise HTTPException(status_code=500, detail=f"Data generation failed: {str(e)}") async def handle_search_components_by_use_case(use_case: str) -> List[Dict[str, Any]]: """Handle searchComponentsByUseCase tool execution""" cache_key = f"use_case_search_{use_case.lower().replace(' ', '_')}" # Check cache first cached_result = await cache_manager.get(cache_key) if cached_result: logger.info(f"Returning cached component suggestions for use case: {use_case}") return cached_result # Generate component suggestions logger.info(f"Searching components for use case: {use_case}") suggestions = await _search_components_by_use_case(use_case) # Cache the result await cache_manager.set(cache_key, suggestions, ttl=3600) return suggestions async def _search_components_by_use_case(use_case: str) -> List[Dict[str, Any]]: """Internal logic for component search by use case""" use_case_lower = use_case.lower() # Pre-defined use case mappings use_case_mappings = { "table": { "components": ["Table", "Simple Table", "Button", "Select", "Input"], "description": "Data display with optional filtering and actions" }, "filter": { "components": ["Select", "Input", "Checkbox", "Button", "Date Picker"], "description": "Filtering and search controls" }, "form": { "components": ["Form", "Form Control", "Input", "Select", "Checkbox", "Button", "Textarea"], "description": "User input and data collection" }, "dashboard": { "components": ["Card", "Container", "Grid", "Stats", "Progress", "Button"], "description": "Overview and summary displays" }, "navigation": { "components": ["Header", "Menu", "Breadcrumb", "Tabs", "Link"], "description": "Site navigation and wayfinding" }, "search": { "components": ["Input", "Button", "Card", "Select", "Autocomplete"], "description": "Search interfaces and results" }, "layout": { "components": ["Container", "Grid", "Stack", "Section", "Divider"], "description": "Page structure and organization" }, "modal": { "components": ["Modal", "Button", "Form", "Card"], "description": "Overlays and dialog boxes" }, "list": { "components": ["Card", "Stack", "Button", "Tag", "Divider"], "description": "Item lists and collections" }, "upload": { "components": ["File Upload", "Button", "Progress", "Alert"], "description": "File handling and upload interfaces" } } suggestions = [] # Find matching use cases for pattern, mapping in use_case_mappings.items(): if pattern in use_case_lower: score = 1.0 # Exact match for component in mapping["components"]: suggestions.append({ "name": component, "description": mapping["description"], "relevanceScore": score, "reason": f"Commonly used in {pattern} interfaces" }) break # If no exact match, try keyword matching if not suggestions: keyword_mappings = { "data": ["Table", "Simple Table", "Card"], "input": ["Input", "Form", "Select", "Textarea"], "button": ["Button", "Utility Button"], "display": ["Card", "Alert", "Info Box"], "select": ["Select", "Dropdown", "Autocomplete"], "date": ["Date Picker", "Date Textbox"], "text": ["Input", "Textarea", "Heading"], "grid": ["Table", "Container", "Grid"], "chart": ["Progress", "Stats"], "menu": ["Header", "Tabs", "Navigation"] } for keyword, components in keyword_mappings.items(): if keyword in use_case_lower: for component in components: suggestions.append({ "name": component, "description": f"Relevant for {keyword}-related interfaces", "relevanceScore": 0.7, "reason": f"Contains keyword: {keyword}" }) # If still no matches, provide general suggestions if not suggestions: general_components = ["Container", "Card", "Button", "Input", "Select"] for component in general_components: suggestions.append({ "name": component, "description": "General-purpose component", "relevanceScore": 0.3, "reason": "Commonly used component" }) # Remove duplicates and sort by relevance score unique_suggestions = {} for suggestion in suggestions: name = suggestion["name"] if name not in unique_suggestions or unique_suggestions[name]["relevanceScore"] < suggestion["relevanceScore"]: unique_suggestions[name] = suggestion return sorted(unique_suggestions.values(), key=lambda x: x["relevanceScore"], reverse=True) async def handle_generate_prototype_code(description: str, components: List[str], include_data: bool = True) -> str: """Handle generatePrototypeCode tool execution""" cache_key = f"prototype_code_{description.lower().replace(' ', '_')}_{len(components)}_{include_data}" # Check cache first cached_result = await cache_manager.get(cache_key) if cached_result: logger.info(f"Returning cached prototype code for: {description}") return cached_result # Generate new code logger.info(f"Generating prototype code for: {description}") code = await _generate_prototype_code(description, components, include_data) # Cache the result await cache_manager.set(cache_key, code, ttl=3600) return code async def _generate_prototype_code(description: str, components: List[str], include_data: bool = True) -> str: """Internal logic for prototype code generation""" description_lower = description.lower() # Determine component type based on description if any(keyword in description_lower for keyword in ["table", "list", "data"]): return _generate_table_component(components, include_data) elif any(keyword in description_lower for keyword in ["form", "input", "submit"]): return _generate_form_component(components, include_data) elif any(keyword in description_lower for keyword in ["dashboard", "summary", "overview"]): return _generate_dashboard_component(components, include_data) else: return _generate_generic_component(description, components, include_data) def _generate_table_component(components: List[str], include_data: bool = True) -> str: """Generate a table component with filtering""" data_import = "" data_code = "" if include_data: data_import = "import { useState, useEffect } from 'react';" data_code = """ const [data, setData] = useState([]); const [filteredData, setFilteredData] = useState([]); const [filter, setFilter] = useState(''); useEffect(() => { // Placeholder data - replace with actual API call const mockData = [ { id: 1, name: 'John Smith', policy: 'Gold Hospital', status: 'Active', premium: 299 }, { id: 2, name: 'Sarah Jones', policy: 'Silver Extras', status: 'Active', premium: 189 }, { id: 3, name: 'Mike Wilson', policy: 'Bronze Combined', status: 'Suspended', premium: 159 }, ]; setData(mockData); setFilteredData(mockData); }, []); useEffect(() => { if (filter) { setFilteredData(data.filter(item => item.name.toLowerCase().includes(filter.toLowerCase()) || item.policy.toLowerCase().includes(filter.toLowerCase()) )); } else { setFilteredData(data); } }, [filter, data]);""" return f"""import React from 'react'; {data_import} import {{ Table, Select, Button, Input, Container }} from '@nib/mesh-ds-react'; const DataTableComponent = () => {{{data_code} const handleFilterChange = (event) => {{ setFilter(event.target.value); }}; const columns = [ {{ key: 'name', title: 'Name' }}, {{ key: 'policy', title: 'Policy Type' }}, {{ key: 'status', title: 'Status' }}, {{ key: 'premium', title: 'Premium ($)' }}, ]; return ( <Container> <div style={{{{ marginBottom: 16 }}}}> <Input placeholder="Search members..." value={{filter}} onChange={{handleFilterChange}} /> </div> <Table columns={{columns}} data={{filteredData}} pagination /> </Container> ); }}; export default DataTableComponent;""" def _generate_form_component(components: List[str], include_data: bool = True) -> str: """Generate a form component with validation""" state_code = "" if include_data: state_code = """ const [formData, setFormData] = useState({ firstName: '', lastName: '', email: '', policyType: '', category: '' }); const handleChange = (field, value) => { setFormData(prev => ({ ...prev, [field]: value })); }; const handleSubmit = (event) => { event.preventDefault(); console.log('Form submitted:', formData); // Add your submission logic here };""" return f"""import React{{ useState }} from 'react'; import {{ Form, FormControl, Input, Select, Button, Container }} from '@nib/mesh-ds-react'; const FormComponent = () => {{{state_code} const policyOptions = [ {{ value: 'hospital', label: 'Hospital Cover' }}, {{ value: 'extras', label: 'Extras Cover' }}, {{ value: 'combined', label: 'Hospital + Extras' }} ]; const categoryOptions = [ {{ value: 'basic', label: 'Basic' }}, {{ value: 'bronze', label: 'Bronze' }}, {{ value: 'silver', label: 'Silver' }}, {{ value: 'gold', label: 'Gold' }} ]; return ( <Container> <Form onSubmit={{handleSubmit}}> <FormControl label="First Name" required> <Input value={{formData.firstName}} onChange={{(e) => handleChange('firstName', e.target.value)}} placeholder="Enter first name" /> </FormControl> <FormControl label="Last Name" required> <Input value={{formData.lastName}} onChange={{(e) => handleChange('lastName', e.target.value)}} placeholder="Enter last name" /> </FormControl> <FormControl label="Email" required> <Input type="email" value={{formData.email}} onChange={{(e) => handleChange('email', e.target.value)}} placeholder="Enter email address" /> </FormControl> <FormControl label="Policy Type" required> <Select options={{policyOptions}} value={{formData.policyType}} onChange={{(value) => handleChange('policyType', value)}} placeholder="Select policy type" /> </FormControl> <FormControl label="Category"> <Select options={{categoryOptions}} value={{formData.category}} onChange={{(value) => handleChange('category', value)}} placeholder="Select category" /> </FormControl> <Button type="submit" variant="primary"> Submit Application </Button> </Form> </Container> ); }}; export default FormComponent;""" def _generate_dashboard_component(components: List[str], include_data: bool = True) -> str: """Generate a dashboard component with summary cards""" return f"""import React from 'react'; import {{ Card, Container, Grid, Stats, Button }} from '@nib/mesh-ds-react'; const DashboardComponent = () => {{ const stats = [ {{ title: 'Total Members', value: '12,543', change: '+5.2%' }}, {{ title: 'Active Policies', value: '9,876', change: '+2.1%' }}, {{ title: 'Claims This Month', value: '1,234', change: '-1.5%' }}, {{ title: 'Revenue YTD', value: '$2.4M', change: '+8.7%' }} ]; return ( <Container> <Grid columns={{4}} gap="medium"> {{stats.map((stat, index) => ( <Card key={{index}}> <Stats title={{stat.title}} value={{stat.value}} change={{stat.change}} /> </Card> ))}} </Grid> <Grid columns={{2}} gap="large" style={{{{ marginTop: 24 }}}}> <Card> <h3>Recent Claims</h3> <p>Claims processing summary and recent activity</p> <Button variant="secondary">View All Claims</Button> </Card> <Card> <h3>Member Activity</h3> <p>Member engagement and policy updates</p> <Button variant="secondary">View Member Reports</Button> </Card> </Grid> </Container> ); }}; export default DashboardComponent;""" def _generate_generic_component(description: str, components: List[str], include_data: bool = True) -> str: """Generate a generic component based on description""" component_imports = ", ".join(components) if components else "Container, Card, Button" return f"""import React from 'react'; import {{ {component_imports} }} from '@nib/mesh-ds-react'; const CustomComponent = () => {{ // Component for: {description} return ( <Container> <Card> <h2>Custom Component</h2> <p>Generated component for: {description}</p> <Button variant="primary">Action Button</Button> </Card> </Container> ); }}; export default CustomComponent;""" if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=8000)

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/lukeylias/MeshMCP'

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