"""MCP Server for Material-UI (MUI) components."""
import json
import time
from typing import Any
from mcp.server import Server
from mcp.types import Tool, TextContent
import mcp.server.stdio
from .mui_fetcher import MUIFetcher
# Initialize fetcher (components fetched dynamically on each request)
_fetcher = MUIFetcher()
# Initialize the MCP server
app = Server("mui-mcp-server")
@app.list_resources()
async def list_resources():
"""List available resources."""
return []
@app.read_resource()
async def read_resource(uri: str):
"""Read a specific resource."""
pass
@app.list_tools()
async def list_tools() -> list[Tool]:
"""List available MUI tools."""
return [
Tool(
name="get_mui_component",
description="Get detailed information about a specific MUI component including props, examples, and usage",
inputSchema={
"type": "object",
"properties": {
"component_name": {
"type": "string",
"description": "The name of the MUI component (e.g., Button, TextField, Card)",
}
},
"required": ["component_name"],
},
),
Tool(
name="search_mui_components",
description="Search for MUI components by keyword or category",
inputSchema={
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search query (e.g., 'input', 'button', 'layout', 'navigation')",
}
},
"required": ["query"],
},
),
Tool(
name="list_all_components",
description="List all available MUI components in the database",
inputSchema={
"type": "object",
"properties": {},
},
),
Tool(
name="mui_health",
description="Health check: fetch components live and report count + latency",
inputSchema={
"type": "object",
"properties": {},
},
),
Tool(
name="generate_mui_component",
description="Generate a complete MUI component with specified props and customization",
inputSchema={
"type": "object",
"properties": {
"component_name": {
"type": "string",
"description": "The MUI component to generate (e.g., Button, TextField)",
},
"props": {
"type": "object",
"description": "Props to apply to the component as key-value pairs",
},
"children": {
"type": "string",
"description": "Content/children for the component",
},
},
"required": ["component_name"],
},
),
]
@app.call_tool()
async def call_tool(name: str, arguments: Any) -> list[TextContent]:
"""Handle tool calls for MUI components."""
# Fetch components dynamically for each request
components = _fetcher.get_components()
if name == "get_mui_component":
component_name = arguments.get("component_name", "")
# Case-insensitive search
component_key = None
for key in components.keys():
if key.lower() == component_name.lower():
component_key = key
break
if not component_key:
return [
TextContent(
type="text",
text=f"Component '{component_name}' not found. Use 'list_all_components' to see available components.",
)
]
component_info = components[component_key]
response = f"""# {component_key}
{component_info['description']}
## Import
```javascript
{component_info['import']}
```
## Props
{json.dumps(component_info['props'], indent=2)}
## Example Usage
```jsx
{component_info['example']}
```
"""
if "subComponents" in component_info:
response += f"\n## Related Components\n{', '.join(component_info['subComponents'])}\n"
return [TextContent(type="text", text=response)]
elif name == "mui_health":
started = time.perf_counter()
comps = _fetcher.get_components()
elapsed_ms = int((time.perf_counter() - started) * 1000)
count = len(comps)
# Try to peek a couple of keys for quick sanity
sample = ", ".join(list(comps.keys())[:5])
response = (
f"MUI Health\n\n"
f"- Components: {count}\n"
f"- Fetch latency: {elapsed_ms} ms\n"
f"- Sample: {sample}"
)
return [TextContent(type="text", text=response)]
elif name == "search_mui_components":
query = arguments.get("query", "").lower()
matching_components = []
for comp_name, comp_info in components.items():
if (
query in comp_name.lower()
or query in comp_info["description"].lower()
):
matching_components.append(
f"- **{comp_name}**: {comp_info['description']}"
)
if not matching_components:
return [
TextContent(
type="text",
text=f"No components found matching '{query}'",
)
]
response = f"# MUI Components matching '{query}':\n\n" + "\n".join(
matching_components
)
return [TextContent(type="text", text=response)]
elif name == "list_all_components":
components_list = []
for comp_name, comp_info in components.items():
components_list.append(f"- **{comp_name}**: {comp_info['description']}")
response = "# All Available MUI Components:\n\n" + "\n".join(components_list)
return [TextContent(type="text", text=response)]
elif name == "generate_mui_component":
component_name = arguments.get("component_name", "")
props = arguments.get("props", {})
children = arguments.get("children", "")
# Case-insensitive search
component_key = None
for key in components.keys():
if key.lower() == component_name.lower():
component_key = key
break
if not component_key:
return [
TextContent(
type="text",
text=f"Component '{component_name}' not found.",
)
]
component_info = components[component_key]
# Generate props string
props_str = " ".join([f'{key}={{{json.dumps(value)}}}' if not isinstance(value, str) else f'{key}="{value}"' for key, value in props.items()])
# Generate component code
if children:
code = f"""import {component_key} from '@mui/material/{component_key}';
<{component_key}{' ' + props_str if props_str else ''}>
{children}
</{component_key}>"""
else:
code = f"""import {component_key} from '@mui/material/{component_key}';
<{component_key}{' ' + props_str if props_str else ''} />"""
return [
TextContent(
type="text",
text=f"# Generated {component_key} Component\n\n```jsx\n{code}\n```",
)
]
return [TextContent(type="text", text=f"Unknown tool: {name}")]
async def run_server():
"""Run the MUI MCP server."""
async with mcp.server.stdio.stdio_server() as (read_stream, write_stream):
await app.run(
read_stream,
write_stream,
app.create_initialization_options(),
)
def main():
"""Entry point for the MCP server."""
import asyncio
asyncio.run(run_server())
if __name__ == "__main__":
main()