@hakxel/mantine-ui-server
Version:
MCP server for working with Mantine UI components - provides documentation, generation, and theme utilities
330 lines (329 loc) • 13 kB
JavaScript
#!/usr/bin/env node
/**
* Mantine UI MCP Server
*
* This server provides tools for working with Mantine UI components, including:
* - Documentation for components
* - Component generation
* - Theme utilities
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js";
// Import utilities and modules
import { initConfig, loadConfigFromEnv, getConfig } from './utils/config.js';
import { getComponentDocs, searchMantineComponents, listMantineComponents } from './documentation/tools.js';
import { generateComponent } from './component-generation/generator.js';
import { generateTheme, generateComponentTheme } from './theme-utils/theme-generator.js';
// Initialize configuration with environment variables
initConfig(loadConfigFromEnv());
// Create MCP server
const server = new Server({
name: "mantine-ui-server",
version: "0.1.0",
}, {
capabilities: {
tools: {},
},
});
/**
* Handler for listing available tools
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: "get_component_docs",
description: "Get documentation for a Mantine component",
inputSchema: {
type: "object",
properties: {
component: {
type: "string",
description: "Name of the Mantine component"
},
section: {
type: "string",
enum: ["props", "examples", "api", "all"],
description: "Section of documentation to retrieve"
},
forceRefresh: {
type: "boolean",
description: "Force refresh of documentation from source"
},
format: {
type: "string",
enum: ["markdown", "json"],
description: "Format of returned documentation"
}
},
required: ["component"]
}
},
{
name: "search_components",
description: "Search for Mantine components",
inputSchema: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query"
}
},
required: ["query"]
}
},
{
name: "list_components",
description: "List all available Mantine components",
inputSchema: {
type: "object",
properties: {}
}
},
{
name: "generate_component",
description: "Generate a Mantine component",
inputSchema: {
type: "object",
properties: {
name: {
type: "string",
description: "Name of the component to generate"
},
mantineComponent: {
type: "string",
description: "Mantine component to base on"
},
props: {
type: "object",
description: "Custom props configuration"
},
styling: {
type: "object",
description: "Styling options"
},
variants: {
type: "array",
items: {
type: "object"
},
description: "Component variants"
},
includeTests: {
type: "boolean",
description: "Generate test file"
}
},
required: ["name", "mantineComponent"]
}
},
{
name: "create_theme_config",
description: "Create a Mantine theme configuration",
inputSchema: {
type: "object",
properties: {
extension: {
type: "string",
enum: ["colors", "components", "typography", "spacing", "radius", "shadows", "other"],
description: "Theme extension type"
},
definitions: {
type: "object",
description: "Theme definitions"
},
darkMode: {
type: "boolean",
description: "Generate dark mode variants"
},
output: {
type: "string",
enum: ["merge", "separate"],
description: "How to output the extension"
}
},
required: ["extension", "definitions"]
}
},
{
name: "create_component_theme",
description: "Create a theme configuration for a specific component",
inputSchema: {
type: "object",
properties: {
component: {
type: "string",
description: "Mantine component to theme"
},
colorScheme: {
type: "string",
enum: ["both", "light", "dark"],
description: "Color schemes to generate"
},
baseColor: {
type: "string",
description: "Base color from your palette"
},
variants: {
type: "array",
items: {
type: "object"
},
description: "Component variants to create"
},
customSelectors: {
type: "object",
description: "Custom CSS selectors to include"
}
},
required: ["component"]
}
}
]
};
});
/**
* Handler for calling tools
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
switch (request.params.name) {
case "get_component_docs": {
const { component, section = 'all', forceRefresh = false, format = 'json' } = request.params.arguments;
if (!component) {
throw new McpError(ErrorCode.InvalidParams, "Component name is required");
}
const docs = await getComponentDocs(component, section, forceRefresh, format);
return {
content: [{
type: "text",
text: typeof docs === 'string' ? docs : JSON.stringify(docs, null, 2)
}]
};
}
case "search_components": {
const { query } = request.params.arguments;
if (!query) {
throw new McpError(ErrorCode.InvalidParams, "Search query is required");
}
const results = await searchMantineComponents(query);
return {
content: [{
type: "text",
text: JSON.stringify(results, null, 2)
}]
};
}
case "list_components": {
const components = await listMantineComponents();
return {
content: [{
type: "text",
text: JSON.stringify(components, null, 2)
}]
};
}
case "generate_component": {
const args = request.params.arguments;
if (!args.name || !args.mantineComponent) {
throw new McpError(ErrorCode.InvalidParams, "Component name and Mantine component are required");
}
const config = {
name: args.name,
mantineComponent: args.mantineComponent,
props: args.props,
styling: args.styling,
variants: args.variants,
includeTests: args.includeTests
};
const result = await generateComponent(config);
return {
content: [{
type: "text",
text: JSON.stringify({
files: result.files,
preview: result.content[`${config.name}.tsx`]
}, null, 2)
}]
};
}
case "create_theme_config": {
const args = request.params.arguments;
if (!args.extension || !args.definitions) {
throw new McpError(ErrorCode.InvalidParams, "Extension type and definitions are required");
}
const config = {
extension: args.extension,
definitions: args.definitions,
darkMode: args.darkMode,
output: args.output
};
const themeConfig = generateTheme(config);
return {
content: [{
type: "text",
text: JSON.stringify(themeConfig, null, 2)
}]
};
}
case "create_component_theme": {
const args = request.params.arguments;
if (!args.component) {
throw new McpError(ErrorCode.InvalidParams, "Component name is required");
}
const config = {
component: args.component,
colorScheme: args.colorScheme,
baseColor: args.baseColor,
variants: args.variants,
customSelectors: args.customSelectors
};
const themeConfig = generateComponentTheme(config);
return {
content: [{
type: "text",
text: JSON.stringify(themeConfig, null, 2)
}]
};
}
default:
throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
}
}
catch (error) {
console.error(`Error executing tool ${request.params.name}:`, error);
return {
content: [{
type: "text",
text: `Error: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
});
/**
* Start the server
*/
async function main() {
console.error(`Starting Mantine UI MCP server...`);
console.error(`Version: ${getConfig().mantineVersion || 'latest'}`);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`Mantine UI MCP server running`);
}
// Handle errors and shutdown
process.on('uncaughtException', (error) => {
console.error('Uncaught exception:', error);
process.exit(1);
});
process.on('SIGINT', async () => {
console.error('Shutting down Mantine UI MCP server...');
await server.close();
process.exit(0);
});
main().catch((error) => {
console.error('Server error:', error);
process.exit(1);
});