From ac3aba8cb871f678e9211b71bc102ec574e2d816 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 1 Aug 2025 23:08:25 +0000 Subject: [PATCH 1/3] Add Anthropic AI SDK and update dependencies Co-authored-by: pratik.jadhav01 --- package-lock.json | 62 ++++++++++++++++++++++++++++++++++++++++++++--- package.json | 1 + 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3bc763c..5b76c80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,8 +7,10 @@ "": { "name": "my-v0-project", "version": "0.1.0", + "hasInstallScript": true, "dependencies": { "@ai-sdk/amazon-bedrock": "^1.0.2", + "@ai-sdk/anthropic": "^2.0.0", "@ai-sdk/google": "^1.2.19", "@ai-sdk/groq": "^1.0.0", "@ai-sdk/openai": "^1.3.22", @@ -129,6 +131,52 @@ } } }, + "node_modules/@ai-sdk/anthropic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-2.0.0.tgz", + "integrity": "sha512-uyyaO4KhxoIKZztREqLPh+6/K3ZJx/rp72JKoUEL9/kC+vfQTThUfPnY/bUryUpcnawx8IY/tSoYNOi/8PCv7w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@ai-sdk/provider-utils": "3.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, + "node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.0.tgz", + "integrity": "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.0.tgz", + "integrity": "sha512-BoQZtGcBxkeSH1zK+SRYNDtJPIPpacTeiMZqnG4Rv6xXjEwM0FH4MGs9c+PlhyEWmQCzjRM2HAotEydFhD4dYw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "2.0.0", + "@standard-schema/spec": "^1.0.0", + "eventsource-parser": "^3.0.3", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.25.76 || ^4" + } + }, "node_modules/@ai-sdk/google": { "version": "1.2.19", "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-1.2.19.tgz", @@ -3706,6 +3754,12 @@ "node": ">=18.0.0" } }, + "node_modules/@standard-schema/spec": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0.tgz", + "integrity": "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==", + "license": "MIT" + }, "node_modules/@standard-schema/utils": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", @@ -4796,12 +4850,12 @@ } }, "node_modules/eventsource-parser": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.2.tgz", - "integrity": "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz", + "integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==", "license": "MIT", "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/express": { diff --git a/package.json b/package.json index 44afdf6..aad6912 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ }, "dependencies": { "@ai-sdk/amazon-bedrock": "^1.0.2", + "@ai-sdk/anthropic": "^2.0.0", "@ai-sdk/google": "^1.2.19", "@ai-sdk/groq": "^1.0.0", "@ai-sdk/openai": "^1.3.22", From fd374c293719a20d78f6938fee88fea7e1fe66d9 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 2 Aug 2025 00:20:28 +0000 Subject: [PATCH 2/3] Update MCP SDK to v1.17, refactor server implementation, and improve error handling Co-authored-by: pratik.jadhav01 --- app/api/chat/route.ts | 25 +- app/api/mcp-chat/route.ts | 2 +- lib/mcp-client.ts | 10 +- mcp-ui-server-v2/package-lock.json | 8 +- mcp-ui-server-v2/package.json | 2 +- mcp-ui-server-v2/src/core/server.ts | 468 ++++++++++------------------ mcp-ui-server-v2/src/index.ts | 8 +- package.json | 5 +- 8 files changed, 194 insertions(+), 334 deletions(-) diff --git a/app/api/chat/route.ts b/app/api/chat/route.ts index d3ffb7b..8e410f0 100644 --- a/app/api/chat/route.ts +++ b/app/api/chat/route.ts @@ -44,26 +44,23 @@ const mcpUITool = tool({ const mcpClient = getMCPClient(); // Call the MCP server to generate the template - const result = await mcpClient.callTool({ - name: 'generate_ui_template', - arguments: { - templateType, - title, - description, - config, - useCase, - theme, - primaryColor, - fullScreen - } + const result = await mcpClient.callTool('generate_ui_template', { + templateType, + title, + description, + config, + useCase, + theme, + primaryColor, + fullScreen }); if (result.isError) { - throw new Error(result.content[0]?.text || 'Unknown MCP error'); + throw new Error(result.content?.[0]?.text || 'Unknown MCP error'); } // Parse the template configuration from MCP response - const templateConfig = JSON.parse(result.content[0]?.text || '{}'); + const templateConfig = JSON.parse(result.content?.[0]?.text || '{}'); return { success: true, diff --git a/app/api/mcp-chat/route.ts b/app/api/mcp-chat/route.ts index 7293149..8a7982f 100644 --- a/app/api/mcp-chat/route.ts +++ b/app/api/mcp-chat/route.ts @@ -264,7 +264,7 @@ The MCP server provides rich, contextual data for each template type. Be convers // Generate response using AI SDK const result = await generateText({ - model, + model: model as any, messages: aiMessages, tools, maxTokens: 2000, diff --git a/lib/mcp-client.ts b/lib/mcp-client.ts index 2b663e0..8fbe597 100644 --- a/lib/mcp-client.ts +++ b/lib/mcp-client.ts @@ -256,7 +256,7 @@ export class MCPClientService { }); // Handle the result according to SDK response format - if (result.content && result.content.length > 0) { + if (result.content && Array.isArray(result.content) && result.content.length > 0) { const content = result.content[0]; if (content.type === 'text') { @@ -273,8 +273,7 @@ export class MCPClientService { metadata: { category: templateData.category || 'general', complexity: templateData.complexity || 'medium', - tags: templateData.tags || [templateType], - toolUsed: generateTool.name + tags: templateData.tags || [templateType] } } }; @@ -331,7 +330,7 @@ export class MCPClientService { } }); - if (result.content && result.content.length > 0) { + if (result.content && Array.isArray(result.content) && result.content.length > 0) { const content = result.content[0]; if (content.type === 'text') { try { @@ -389,10 +388,11 @@ export class MCPClientService { } try { - return await this.client.callTool({ + const result = await this.client.callTool({ name: toolName, arguments: arguments_ }); + return result as any; } catch (error) { console.error(`Failed to call tool ${toolName}:`, error); throw error; diff --git a/mcp-ui-server-v2/package-lock.json b/mcp-ui-server-v2/package-lock.json index 5eaba31..738fc07 100644 --- a/mcp-ui-server-v2/package-lock.json +++ b/mcp-ui-server-v2/package-lock.json @@ -9,7 +9,7 @@ "version": "2.0.0", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.12.1", + "@modelcontextprotocol/sdk": "^1.17.0", "date-fns": "^3.6.0", "dotenv": "^16.4.5", "lodash": "^4.17.21", @@ -810,9 +810,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.16.0", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.16.0.tgz", - "integrity": "sha512-8ofX7gkZcLj9H9rSd50mCgm3SSF8C7XoclxJuLoV0Cz3rEQ1tv9MZRYYvJtm9n1BiEQQMzSmE/w2AEkNacLYfg==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.1.tgz", + "integrity": "sha512-CPle1OQehbWqd25La9Ack5B07StKIxh4+Bf19qnpZKJC1oI22Y0czZHbifjw1UoczIfKBwBDAp/dFxvHG13B5A==", "license": "MIT", "dependencies": { "ajv": "^6.12.6", diff --git a/mcp-ui-server-v2/package.json b/mcp-ui-server-v2/package.json index b77390c..c3ba5fb 100644 --- a/mcp-ui-server-v2/package.json +++ b/mcp-ui-server-v2/package.json @@ -43,7 +43,7 @@ "author": "Dynamic Templates Team", "license": "MIT", "dependencies": { - "@modelcontextprotocol/sdk": "^1.12.1", + "@modelcontextprotocol/sdk": "^1.17.0", "zod": "^3.23.8", "uuid": "^10.0.0", "date-fns": "^3.6.0", diff --git a/mcp-ui-server-v2/src/core/server.ts b/mcp-ui-server-v2/src/core/server.ts index ddc7540..ecdf918 100644 --- a/mcp-ui-server-v2/src/core/server.ts +++ b/mcp-ui-server-v2/src/core/server.ts @@ -3,18 +3,8 @@ * Follows MCP specification v2024-11-05 with comprehensive features */ -import { Server } from '@modelcontextprotocol/sdk/server/index.js'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { - ListToolsRequestSchema, - CallToolRequestSchema, - ListResourcesRequestSchema, - ReadResourceRequestSchema, - SubscribeRequestSchema, - UnsubscribeRequestSchema, - ListPromptsRequestSchema, - GetPromptRequestSchema, -} from '@modelcontextprotocol/sdk/types.js'; import type { ServerConfig, ServerCapabilities, @@ -33,9 +23,13 @@ import { ToolManager } from '../tools/manager.js'; import { ResourceManager } from '../resources/manager.js'; import { PromptManager } from '../prompts/manager.js'; import { ValidationError, MCPError, ProtocolError } from '../utils/errors.js'; +import { z } from 'zod'; +/** + * Production-ready MCP Server with comprehensive features + */ export class MCPServer { - private server: Server; + private server: McpServer; private config: ServerConfig; private logger = getLogger(); private cache = getCache(); @@ -48,378 +42,242 @@ export class MCPServer { constructor(config: ServerConfig) { this.config = config; - // Initialize server with capabilities - this.server = new Server( - { - name: config.name, - version: config.version, - }, - { - capabilities: config.capabilities as any, - } - ); + // Initialize server with new API + this.server = new McpServer({ + name: config.name, + version: config.version, + }); // Initialize managers this.toolManager = new ToolManager(); this.resourceManager = new ResourceManager(); this.promptManager = new PromptManager(); - this.setupHandlers(); this.setupErrorHandling(); } /** - * Set up all MCP protocol handlers + * Set up all MCP protocol handlers using the new register* API */ - private setupHandlers(): void { - this.setupToolHandlers(); - this.setupResourceHandlers(); - this.setupPromptHandlers(); - this.setupConnectionHandlers(); + private async setupHandlers(): Promise { + await this.setupToolHandlers(); + await this.setupResourceHandlers(); + await this.setupPromptHandlers(); } /** * Set up tool-related handlers */ - private setupToolHandlers(): void { - // List available tools - this.server.setRequestHandler(ListToolsRequestSchema, async () => { - try { - const startTime = Date.now(); - const tools = await this.toolManager.listTools(); - const duration = Date.now() - startTime; - - this.logger.performance('tools/list', duration, { toolCount: tools.length }); - - return { tools }; - } catch (error) { - this.logger.error('Failed to list tools', error); - throw new MCPError(-32603, 'Internal error listing tools'); - } - }); - - // Call a tool - this.server.setRequestHandler(CallToolRequestSchema, async (request) => { - const startTime = Date.now(); - let toolName = 'unknown'; - - try { - const { name, arguments: args } = request.params as ToolCall; - toolName = name; - - this.logger.toolCall(name, args || {}); - - // Check cache first - const cacheKey = this.cache.generateKey(`tool:${name}`, args || {}); - const cached = this.cache.get(cacheKey); - - if (cached) { - const duration = Date.now() - startTime; - this.logger.toolResult(name, true, duration); - return cached; - } - - // Execute tool - const result = await this.toolManager.callTool(name, args || {}); - const duration = Date.now() - startTime; - - // Cache successful results - if (!result.isError) { - this.cache.set(cacheKey, result, 300); // 5 minute cache - } - - this.logger.toolResult(name, !result.isError, duration, result.isError ? new Error('Tool execution failed') : undefined); - - return result as any; - } catch (error) { - const duration = Date.now() - startTime; - this.logger.toolResult(toolName, false, duration, error as Error); - - if (error instanceof ValidationError) { - throw new MCPError(-32602, `Invalid parameters: ${error.message}`); + private async setupToolHandlers(): Promise { + // Initialize the tool manager first + await this.toolManager.initialize(); + + // Get all tools from the tool manager + const tools = await this.toolManager.listTools(); + + for (const tool of tools) { + this.server.registerTool( + tool.name, + { + description: tool.description || `Tool: ${tool.name}`, + inputSchema: tool.inputSchema as any, + }, + async (args: any) => { + try { + const startTime = Date.now(); + const result = await this.toolManager.callTool(tool.name, args); + const duration = Date.now() - startTime; + + this.logger.performance(`tools/${tool.name}`, duration, { + toolName: tool.name, + success: !result.isError + }); + + return result; + } catch (error) { + this.logger.error(`Tool execution failed: ${tool.name}`, error); + throw new MCPError(-32603, 'Internal error executing tool'); + } } - - this.logger.error(`Tool execution failed: ${toolName}`, error); - throw new MCPError(-32603, 'Internal error executing tool'); - } - }); + ); + } } /** * Set up resource-related handlers */ - private setupResourceHandlers(): void { - // List available resources - this.server.setRequestHandler(ListResourcesRequestSchema, async () => { - try { - const startTime = Date.now(); - const resources = await this.resourceManager.listResources(); - const duration = Date.now() - startTime; - - this.logger.performance('resources/list', duration, { resourceCount: resources.length }); - - return { resources }; - } catch (error) { - this.logger.error('Failed to list resources', error); - throw new MCPError(-32603, 'Internal error listing resources'); - } - }); - - // Read a resource - this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => { - try { - const { uri } = request.params as { uri: string }; - - // Check cache first - const cached = this.cache.getCachedResource(uri); - if (cached) { - this.logger.resourceAccess(uri, true, true); - return cached; - } - - const content = await this.resourceManager.readResource(uri); - - // Cache the result - this.cache.cacheResource(uri, content, 600); // 10 minute cache - - this.logger.resourceAccess(uri, true, false); - return content as any; - } catch (error) { - this.logger.error(`Failed to read resource: ${request.params?.uri}`, error); - throw new MCPError(-32603, 'Internal error reading resource'); - } - }); - - // Subscribe to resource changes (if supported) - if (this.config.capabilities.resources?.subscribe) { - this.server.setRequestHandler('resources/subscribe' as any, async (request: any) => { - try { - const { uri } = request.params as { uri: string }; - await this.resourceManager.subscribe(uri); - this.logger.info('Resource subscription created', { uri }); - return {}; - } catch (error) { - this.logger.error(`Failed to subscribe to resource: ${request.params?.uri}`, error); - throw new MCPError(-32603, 'Internal error subscribing to resource'); - } - }); - - this.server.setRequestHandler('resources/unsubscribe' as any, async (request: any) => { - try { - const { uri } = request.params as { uri: string }; - await this.resourceManager.unsubscribe(uri); - this.logger.info('Resource subscription removed', { uri }); - return {}; - } catch (error) { - this.logger.error(`Failed to unsubscribe from resource: ${request.params?.uri}`, error); - throw new MCPError(-32603, 'Internal error unsubscribing from resource'); + private async setupResourceHandlers(): Promise { + // Initialize the resource manager first + await this.resourceManager.initialize(); + + // Get all resources from the resource manager + const resources = await this.resourceManager.listResources(); + + for (const resource of resources) { + this.server.registerResource( + resource.name, + resource.uri, + { + description: resource.description || `Resource: ${resource.name}`, + mimeType: resource.mimeType, + }, + async (resourceUri) => { + try { + const startTime = Date.now(); + const result = await this.resourceManager.readResource(resourceUri.href); + const duration = Date.now() - startTime; + + this.logger.performance(`resources/${resource.name}`, duration, { + resourceName: resource.name, + uri: resourceUri.href + }); + + // Convert ResourceContents to the expected format + return { + contents: result.contents || [] + }; + } catch (error) { + this.logger.error(`Resource read failed: ${resource.name}`, error); + throw new MCPError(-32603, 'Internal error reading resource'); + } } - }); + ); } } /** * Set up prompt-related handlers */ - private setupPromptHandlers(): void { - // List available prompts - this.server.setRequestHandler(ListPromptsRequestSchema, async () => { - try { - const startTime = Date.now(); - const prompts = await this.promptManager.listPrompts(); - const duration = Date.now() - startTime; - - this.logger.performance('prompts/list', duration, { promptCount: prompts.length }); - - return { prompts }; - } catch (error) { - this.logger.error('Failed to list prompts', error); - throw new MCPError(-32603, 'Internal error listing prompts'); - } - }); - - // Get a prompt - this.server.setRequestHandler(GetPromptRequestSchema, async (request) => { - try { - const { name, arguments: args } = request.params as { name: string; arguments?: Record }; - - const result = await this.promptManager.getPrompt(name, args || {}); - - this.logger.promptGeneration(name, args || {}); - - return result as any; - } catch (error) { - this.logger.error(`Failed to get prompt: ${request.params?.name}`, error); - throw new MCPError(-32603, 'Internal error getting prompt'); - } - }); - } - - /** - * Set up connection and lifecycle handlers - */ - private setupConnectionHandlers(): void { - // Handle ping requests - this.server.setRequestHandler('ping' as any, async () => { - return { status: 'pong', timestamp: new Date().toISOString() }; - }); - - // Handle initialization - this.server.setRequestHandler('initialize' as any, async (request: any) => { - try { - const { protocolVersion, clientInfo, capabilities } = request.params as { - protocolVersion: string; - clientInfo: { name: string; version: string }; - capabilities: any; - }; - - // Validate protocol version - if (protocolVersion !== '2024-11-05') { - throw new ProtocolError(`Unsupported protocol version: ${protocolVersion}`); + private async setupPromptHandlers(): Promise { + // Initialize the prompt manager first + await this.promptManager.initialize(); + + // Get all prompts from the prompt manager + const prompts = await this.promptManager.listPrompts(); + + for (const prompt of prompts) { + this.server.registerPrompt( + prompt.name, + { + description: prompt.description || `Prompt: ${prompt.name}`, + argsSchema: prompt.arguments as any, + }, + async (args: any) => { + try { + const startTime = Date.now(); + const result = await this.promptManager.getPrompt(prompt.name, args); + const duration = Date.now() - startTime; + + this.logger.performance(`prompts/${prompt.name}`, duration, { + promptName: prompt.name + }); + + return result; + } catch (error) { + this.logger.error(`Prompt execution failed: ${prompt.name}`, error); + throw new MCPError(-32603, 'Internal error executing prompt'); + } } - - // Store client connection info - const clientId = `${clientInfo.name}-${Date.now()}`; - this.clientConnections.set(clientId, { - id: clientId, - capabilities, - connectedAt: new Date(), - }); - - this.logger.connectionEvent('connect', clientId); - this.isInitialized = true; - - return { - protocolVersion: '2024-11-05' as const, - capabilities: this.config.capabilities, - serverInfo: { - name: this.config.name, - version: this.config.version, - }, - }; - } catch (error) { - this.logger.error('Initialization failed', error); - throw new MCPError(-32603, 'Initialization failed'); - } - }); + ); + } } /** * Set up error handling */ private setupErrorHandling(): void { + // Handle uncaught errors process.on('uncaughtException', (error) => { this.logger.error('Uncaught exception', error); - this.shutdown(); + process.exit(1); }); process.on('unhandledRejection', (reason, promise) => { - this.logger.error('Unhandled rejection', reason); + this.logger.error('Unhandled rejection', { reason, promise }); }); + } - process.on('SIGINT', () => { - this.logger.info('Received SIGINT, shutting down gracefully'); - this.shutdown(); - }); - process.on('SIGTERM', () => { - this.logger.info('Received SIGTERM, shutting down gracefully'); - this.shutdown(); - }); - } /** - * Register tools, resources, and prompts + * Connect to a transport and start the server */ - async registerComponents(): Promise { + async connect(transport: StdioServerTransport): Promise { try { - // Register all tools - await this.toolManager.initialize(); + this.logger.info('Connecting MCP server to transport'); - // Register all resources - await this.resourceManager.initialize(); + // Set up handlers first + await this.setupHandlers(); - // Register all prompts - await this.promptManager.initialize(); - - this.logger.info('All components registered successfully'); - } catch (error) { - this.logger.error('Failed to register components', error); - throw error; - } - } - - /** - * Start the server - */ - async start(): Promise { - try { - await this.registerComponents(); - - const transport = new StdioServerTransport(); await this.server.connect(transport); + this.isInitialized = true; - this.logger.info('MCP Server started successfully', { + const tools = await this.toolManager.listTools(); + const resources = await this.resourceManager.listResources(); + const prompts = await this.promptManager.listPrompts(); + + this.logger.info('MCP server connected successfully', { name: this.config.name, version: this.config.version, - capabilities: this.config.capabilities, + toolCount: tools.length, + resourceCount: resources.length, + promptCount: prompts.length, }); } catch (error) { - this.logger.error('Failed to start server', error); + this.logger.error('Failed to connect MCP server', error); throw error; } } /** - * Shutdown the server gracefully + * Close the server and cleanup resources */ - async shutdown(): Promise { + async close(): Promise { try { - this.logger.info('Shutting down MCP server'); - - // Notify all connected clients about shutdown - for (const [clientId] of this.clientConnections) { - this.logger.connectionEvent('disconnect', clientId); - } + this.logger.info('Closing MCP server'); - // Clear cache + // Clear caches this.cache.clear(); - // Close any open connections - await this.resourceManager.cleanup(); + // Clear connections + this.clientConnections.clear(); + + this.isInitialized = false; + this.logger.info('MCP server closed successfully'); - this.logger.info('MCP server shutdown complete'); - process.exit(0); } catch (error) { - this.logger.error('Error during shutdown', error); - process.exit(1); + this.logger.error('Error closing MCP server', error); + throw error; } } /** - * Get server status + * Get server statistics */ - getStatus(): { + async getStats(): Promise<{ + name: string; + version: string; isInitialized: boolean; - connections: number; + toolCount: number; + resourceCount: number; + promptCount: number; + connectionCount: number; uptime: number; - cacheStats: any; - } { + }> { + const tools = await this.toolManager.listTools(); + const resources = await this.resourceManager.listResources(); + const prompts = await this.promptManager.listPrompts(); + return { + name: this.config.name, + version: this.config.version, isInitialized: this.isInitialized, - connections: this.clientConnections.size, + toolCount: tools.length, + resourceCount: resources.length, + promptCount: prompts.length, + connectionCount: this.clientConnections.size, uptime: process.uptime(), - cacheStats: this.cache.getStats(), }; } - - /** - * Send notification to all connected clients - */ - async notifyClients(method: string, params?: Record): Promise { - // Implementation would depend on transport type - // For stdio, notifications are not typically sent back to client - this.logger.debug('Client notification', { method, params }); - } } \ No newline at end of file diff --git a/mcp-ui-server-v2/src/index.ts b/mcp-ui-server-v2/src/index.ts index 1b9ef29..c481239 100644 --- a/mcp-ui-server-v2/src/index.ts +++ b/mcp-ui-server-v2/src/index.ts @@ -15,6 +15,7 @@ import 'dotenv/config'; import { MCPServer } from './core/server.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { createDefaultLogger, setDefaultLogger } from './core/logger.js'; import { createDefaultCache, setDefaultCache } from './core/cache.js'; import type { ServerConfig } from './types/mcp.js'; @@ -143,17 +144,18 @@ async function initialize(): Promise { async function main(): Promise { try { const server = await initialize(); - await server.start(); + const transport = new StdioServerTransport(); + await server.connect(transport); // Keep the process alive process.on('SIGINT', () => { console.error('\nReceived SIGINT, shutting down gracefully...'); - server.shutdown(); + server.close(); }); process.on('SIGTERM', () => { console.error('\nReceived SIGTERM, shutting down gracefully...'); - server.shutdown(); + server.close(); }); } catch (error) { diff --git a/package.json b/package.json index aad6912..de75d28 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,12 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "npm run mcp:start && next dev", + "dev": "next dev", + "dev:with-mcp": "npm run mcp:start && next dev", "build": "npm run mcp:build && next build", + "build:web-only": "next build", "start": "npm run mcp:start && next start", + "start:web-only": "next start", "lint": "next lint", "test-bedrock": "node test-bedrock.mjs", "mcp:build": "cd mcp-ui-server-v2 && npm install && npm run build", From 487585f7dc60793a2d9a4b10c23e71e560fbf5d4 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sat, 2 Aug 2025 00:26:19 +0000 Subject: [PATCH 3/3] Remove core server modules and simplify server entry point Co-authored-by: pratik.jadhav01 --- .mcp-server.log | 4 + mcp-ui-server-v2/package.json | 8 +- mcp-ui-server-v2/src/core/cache.ts | 334 ---- mcp-ui-server-v2/src/core/logger.ts | 187 --- mcp-ui-server-v2/src/core/server.ts | 283 ---- mcp-ui-server-v2/src/index.ts | 189 --- mcp-ui-server-v2/src/prompts/manager.ts | 324 ---- mcp-ui-server-v2/src/resources/manager.ts | 412 ----- mcp-ui-server-v2/src/simple-server.ts | 353 +++++ mcp-ui-server-v2/src/templates/engine.ts | 428 ----- .../src/templates/generators/analytics.ts | 66 - .../src/templates/generators/blog.ts | 44 - .../src/templates/generators/calendar.ts | 55 - .../src/templates/generators/chart.ts | 77 - .../src/templates/generators/dashboard.ts | 733 --------- .../src/templates/generators/data-table.ts | 1406 ----------------- .../src/templates/generators/ecommerce.ts | 44 - .../src/templates/generators/feed.ts | 62 - .../src/templates/generators/form.ts | 1066 ------------- .../src/templates/generators/gallery.ts | 111 -- .../src/templates/generators/kanban.ts | 81 - .../src/templates/generators/map.ts | 44 - .../src/templates/generators/marketplace.ts | 44 - .../src/templates/generators/portfolio.ts | 44 - .../src/templates/generators/pricing.ts | 44 - .../templates/generators/product-catalog.ts | 439 ----- .../src/templates/generators/profile-card.ts | 44 - .../src/templates/generators/sample-data.ts | 565 ------- .../src/templates/generators/stats.ts | 70 - .../src/templates/generators/timeline.ts | 70 - .../src/templates/generators/wizard.ts | 44 - mcp-ui-server-v2/src/tools/manager.ts | 465 ------ mcp-ui-server-v2/src/types/mcp.ts | 375 ----- mcp-ui-server-v2/src/utils/errors.ts | 348 ---- scripts/start-mcp-server.js | 22 +- 35 files changed, 377 insertions(+), 8508 deletions(-) create mode 100644 .mcp-server.log delete mode 100644 mcp-ui-server-v2/src/core/cache.ts delete mode 100644 mcp-ui-server-v2/src/core/logger.ts delete mode 100644 mcp-ui-server-v2/src/core/server.ts delete mode 100644 mcp-ui-server-v2/src/index.ts delete mode 100644 mcp-ui-server-v2/src/prompts/manager.ts delete mode 100644 mcp-ui-server-v2/src/resources/manager.ts create mode 100644 mcp-ui-server-v2/src/simple-server.ts delete mode 100644 mcp-ui-server-v2/src/templates/engine.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/analytics.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/blog.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/calendar.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/chart.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/dashboard.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/data-table.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/ecommerce.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/feed.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/form.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/gallery.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/kanban.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/map.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/marketplace.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/portfolio.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/pricing.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/product-catalog.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/profile-card.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/sample-data.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/stats.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/timeline.ts delete mode 100644 mcp-ui-server-v2/src/templates/generators/wizard.ts delete mode 100644 mcp-ui-server-v2/src/tools/manager.ts delete mode 100644 mcp-ui-server-v2/src/types/mcp.ts delete mode 100644 mcp-ui-server-v2/src/utils/errors.ts diff --git a/.mcp-server.log b/.mcp-server.log new file mode 100644 index 0000000..d455078 --- /dev/null +++ b/.mcp-server.log @@ -0,0 +1,4 @@ +[STDERR] 🚀 Starting Dynamic Templates MCP Server v2.0... +[STDERR] ✅ MCP Server connected and ready! +📋 Available tools: generate_template, list_template_types, get_template_suggestions +🎨 Template types: 20 available diff --git a/mcp-ui-server-v2/package.json b/mcp-ui-server-v2/package.json index c3ba5fb..7fb9ac5 100644 --- a/mcp-ui-server-v2/package.json +++ b/mcp-ui-server-v2/package.json @@ -2,15 +2,15 @@ "name": "@dynamic-templates/mcp-server", "version": "2.0.0", "description": "Production-ready MCP server for dynamic UI template generation with comprehensive template support", - "main": "dist/index.js", + "main": "dist/simple-server.js", "type": "module", "engines": { "node": ">=18.0.0" }, "scripts": { - "dev": "tsx watch --clear-screen=false src/index.ts", + "dev": "tsx watch --clear-screen=false src/simple-server.ts", "build": "tsc && npm run copy-resources", - "start": "node dist/index.js", + "start": "node dist/simple-server.js", "clean": "rimraf dist", "copy-resources": "cp -r src/resources dist/ 2>/dev/null || true", "test": "vitest", @@ -23,7 +23,7 @@ "debug": "tsx --inspect src/index.ts" }, "bin": { - "dynamic-templates-mcp": "dist/index.js" + "dynamic-templates-mcp": "dist/simple-server.js" }, "files": [ "dist", diff --git a/mcp-ui-server-v2/src/core/cache.ts b/mcp-ui-server-v2/src/core/cache.ts deleted file mode 100644 index 14fa4b1..0000000 --- a/mcp-ui-server-v2/src/core/cache.ts +++ /dev/null @@ -1,334 +0,0 @@ -/** - * Intelligent caching system for MCP server - * Provides multiple caching strategies and automatic invalidation - */ - -import NodeCache from 'node-cache'; -import crypto from 'crypto'; -import type { CacheConfig } from '../types/mcp.js'; -import { getLogger } from './logger.js'; - -export interface CacheEntry { - value: T; - timestamp: number; - hits: number; - size: number; -} - -export interface CacheStats { - hits: number; - misses: number; - sets: number; - deletes: number; - size: number; - keys: number; - hitRate: number; -} - -export class Cache { - private cache: NodeCache; - private config: CacheConfig; - private stats: CacheStats; - private logger = getLogger(); - - constructor(config: CacheConfig) { - this.config = config; - this.stats = { - hits: 0, - misses: 0, - sets: 0, - deletes: 0, - size: 0, - keys: 0, - hitRate: 0, - }; - - if (!config.enabled) { - // Create a no-op cache if disabled - this.cache = new NodeCache({ stdTTL: 0, checkperiod: 0 }); - return; - } - - this.cache = new NodeCache({ - stdTTL: config.ttl, - checkperiod: Math.floor(config.ttl / 2), - useClones: false, - maxKeys: config.maxSize, - }); - - // Set up event listeners for cache operations - this.cache.on('set', (key: string, value: unknown) => { - this.stats.sets++; - this.stats.keys = this.cache.keys().length; - this.updateHitRate(); - this.logger.debug('Cache set', { key, type: 'cache_set' }); - }); - - this.cache.on('del', (key: string, value: unknown) => { - this.stats.deletes++; - this.stats.keys = this.cache.keys().length; - this.logger.debug('Cache delete', { key, type: 'cache_delete' }); - }); - - this.cache.on('expired', (key: string, value: unknown) => { - this.stats.deletes++; - this.stats.keys = this.cache.keys().length; - this.logger.debug('Cache expired', { key, type: 'cache_expired' }); - }); - } - - /** - * Generate a cache key from parameters - */ - generateKey(prefix: string, params: Record): string { - const sortedParams = Object.keys(params) - .sort() - .reduce((result, key) => { - result[key] = params[key]; - return result; - }, {} as Record); - - const hash = crypto - .createHash('sha256') - .update(JSON.stringify(sortedParams)) - .digest('hex') - .substring(0, 16); - - return `${prefix}:${hash}`; - } - - /** - * Get value from cache - */ - get(key: string): T | undefined { - if (!this.config.enabled) { - return undefined; - } - - const value = this.cache.get(key); - - if (value !== undefined) { - this.stats.hits++; - this.logger.debug('Cache hit', { key, type: 'cache_hit' }); - } else { - this.stats.misses++; - this.logger.debug('Cache miss', { key, type: 'cache_miss' }); - } - - this.updateHitRate(); - return value; - } - - /** - * Set value in cache - */ - set(key: string, value: T, ttl?: number): boolean { - if (!this.config.enabled) { - return false; - } - - const success = this.cache.set(key, value, ttl || this.config.ttl); - - if (success) { - this.stats.size += this.estimateSize(value); - } - - return success; - } - - /** - * Delete value from cache - */ - delete(key: string): number { - if (!this.config.enabled) { - return 0; - } - - const deleted = this.cache.del(key); - return deleted; - } - - /** - * Check if key exists in cache - */ - has(key: string): boolean { - if (!this.config.enabled) { - return false; - } - - return this.cache.has(key); - } - - /** - * Get cache statistics - */ - getStats(): CacheStats { - return { ...this.stats }; - } - - /** - * Clear all cache entries - */ - clear(): void { - if (!this.config.enabled) { - return; - } - - this.cache.flushAll(); - this.stats.deletes += this.stats.keys; - this.stats.keys = 0; - this.stats.size = 0; - this.logger.info('Cache cleared', { type: 'cache_clear' }); - } - - /** - * Get or set pattern - if key exists, return it, otherwise compute and cache - */ - async getOrSet( - key: string, - computeFn: () => Promise | T, - ttl?: number - ): Promise { - const cached = this.get(key); - - if (cached !== undefined) { - return cached; - } - - const value = await computeFn(); - this.set(key, value, ttl); - return value; - } - - /** - * Cache template generation results - */ - cacheTemplate(templateType: string, params: Record, result: unknown, ttl?: number): void { - const key = this.generateKey(`template:${templateType}`, params); - this.set(key, result, ttl); - } - - /** - * Get cached template - */ - getCachedTemplate(templateType: string, params: Record): T | undefined { - const key = this.generateKey(`template:${templateType}`, params); - return this.get(key); - } - - /** - * Cache data source results - */ - cacheDataSource(sourceId: string, query: string, result: unknown, ttl?: number): void { - const key = this.generateKey(`datasource:${sourceId}`, { query }); - this.set(key, result, ttl); - } - - /** - * Get cached data source result - */ - getCachedDataSource(sourceId: string, query: string): T | undefined { - const key = this.generateKey(`datasource:${sourceId}`, { query }); - return this.get(key); - } - - /** - * Cache resource content - */ - cacheResource(uri: string, content: unknown, ttl?: number): void { - const key = `resource:${uri}`; - this.set(key, content, ttl); - } - - /** - * Get cached resource - */ - getCachedResource(uri: string): T | undefined { - const key = `resource:${uri}`; - return this.get(key); - } - - /** - * Invalidate cache entries by pattern - */ - invalidatePattern(pattern: string): number { - if (!this.config.enabled) { - return 0; - } - - const keys = this.cache.keys(); - const matchingKeys = keys.filter(key => key.includes(pattern)); - - let deleted = 0; - for (const key of matchingKeys) { - deleted += this.cache.del(key); - } - - this.logger.info('Cache pattern invalidated', { - pattern, - deletedKeys: deleted, - type: 'cache_invalidate' - }); - - return deleted; - } - - /** - * Get all cache keys - */ - getKeys(): string[] { - return this.cache.keys(); - } - - /** - * Get cache key TTL - */ - getTtl(key: string): number | undefined { - if (!this.config.enabled) { - return undefined; - } - - return this.cache.getTtl(key); - } - - private updateHitRate(): void { - const total = this.stats.hits + this.stats.misses; - this.stats.hitRate = total > 0 ? this.stats.hits / total : 0; - } - - private estimateSize(value: unknown): number { - try { - return JSON.stringify(value).length * 2; // Rough estimate in bytes - } catch { - return 100; // Default size estimate - } - } -} - -// Default cache instance -let defaultCache: Cache | null = null; - -export function createCache(config: CacheConfig): Cache { - return new Cache(config); -} - -export function setDefaultCache(cache: Cache): void { - defaultCache = cache; -} - -export function getCache(): Cache { - if (!defaultCache) { - throw new Error('Cache not initialized. Call setDefaultCache() first.'); - } - return defaultCache; -} - -// Convenience function for creating a cache with sensible defaults -export function createDefaultCache(): Cache { - return createCache({ - enabled: true, - ttl: 300, // 5 minutes - maxSize: 1000, - strategy: 'lru', - }); -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/core/logger.ts b/mcp-ui-server-v2/src/core/logger.ts deleted file mode 100644 index 04e4f55..0000000 --- a/mcp-ui-server-v2/src/core/logger.ts +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Centralized logging system for MCP server - * Provides structured logging with multiple levels and formats - */ - -import pino from 'pino'; -import type { LoggingConfig } from '../types/mcp.js'; - -export class Logger { - private logger: pino.Logger; - private config: LoggingConfig; - - constructor(config: LoggingConfig) { - this.config = config; - - const pinoConfig: pino.LoggerOptions = { - level: config.level, - ...(config.format === 'pretty' - ? { - transport: { - target: 'pino-pretty', - options: { - colorize: true, - translateTime: 'HH:MM:ss Z', - ignore: 'pid,hostname', - }, - }, - } - : {}), - }; - - if (config.destination) { - pinoConfig.transport = { - target: 'pino/file', - options: { - destination: config.destination, - }, - }; - } - - this.logger = pino(pinoConfig); - } - - debug(message: string, meta?: Record): void { - this.logger.debug(meta, message); - } - - info(message: string, meta?: Record): void { - this.logger.info(meta, message); - } - - warn(message: string, meta?: Record): void { - this.logger.warn(meta, message); - } - - error(message: string, error?: Error | unknown, meta?: Record): void { - const errorMeta = { - ...meta, - ...(error instanceof Error - ? { - error: { - name: error.name, - message: error.message, - stack: error.stack, - }, - } - : error ? { error } : {}), - }; - this.logger.error(errorMeta, message); - } - - // MCP-specific logging methods - toolCall(toolName: string, params: Record, duration?: number): void { - this.info('Tool called', { - toolName, - params, - duration, - type: 'tool_call', - }); - } - - toolResult(toolName: string, success: boolean, duration: number, error?: Error): void { - const level = success ? 'info' : 'error'; - this.logger[level]({ - toolName, - success, - duration, - error: error ? { - name: error.name, - message: error.message, - } : undefined, - type: 'tool_result', - }, `Tool ${success ? 'completed' : 'failed'}: ${toolName}`); - } - - resourceAccess(uri: string, success: boolean, cached: boolean = false): void { - this.info('Resource accessed', { - uri, - success, - cached, - type: 'resource_access', - }); - } - - promptGeneration(promptName: string, params: Record): void { - this.info('Prompt generated', { - promptName, - params, - type: 'prompt_generation', - }); - } - - connectionEvent(event: 'connect' | 'disconnect' | 'error', clientId?: string, error?: Error): void { - const level = event === 'error' ? 'error' : 'info'; - this.logger[level]({ - event, - clientId, - error: error ? { - name: error.name, - message: error.message, - } : undefined, - type: 'connection_event', - }, `Client ${event}${clientId ? ` (${clientId})` : ''}`); - } - - protocolEvent(method: string, direction: 'inbound' | 'outbound', success: boolean, duration?: number): void { - this.debug('Protocol message', { - method, - direction, - success, - duration, - type: 'protocol_event', - }); - } - - performance(operation: string, duration: number, metadata?: Record): void { - this.info('Performance metric', { - operation, - duration, - ...metadata, - type: 'performance', - }); - } - - security(event: string, severity: 'low' | 'medium' | 'high', details?: Record): void { - const level = severity === 'high' ? 'error' : severity === 'medium' ? 'warn' : 'info'; - this.logger[level]({ - event, - severity, - ...details, - type: 'security', - }, `Security event: ${event}`); - } - - child(bindings: Record): Logger { - const childLogger = this.logger.child(bindings); - const newLogger = Object.create(this); - newLogger.logger = childLogger; - return newLogger; - } -} - -// Default logger instance -let defaultLogger: Logger | null = null; - -export function createLogger(config: LoggingConfig): Logger { - return new Logger(config); -} - -export function setDefaultLogger(logger: Logger): void { - defaultLogger = logger; -} - -export function getLogger(): Logger { - if (!defaultLogger) { - throw new Error('Logger not initialized. Call setDefaultLogger() first.'); - } - return defaultLogger; -} - -// Convenience function for creating a logger with sensible defaults -export function createDefaultLogger(level: LoggingConfig['level'] = 'info'): Logger { - return createLogger({ - level, - format: process.env.NODE_ENV === 'development' ? 'pretty' : 'json', - }); -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/core/server.ts b/mcp-ui-server-v2/src/core/server.ts deleted file mode 100644 index ecdf918..0000000 --- a/mcp-ui-server-v2/src/core/server.ts +++ /dev/null @@ -1,283 +0,0 @@ -/** - * Production-ready MCP Server implementation - * Follows MCP specification v2024-11-05 with comprehensive features - */ - -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import type { - ServerConfig, - ServerCapabilities, - Tool, - Resource, - Prompt, - ToolCall, - ToolResult, - ResourceContents, - GetPromptResult, -} from '../types/mcp.js'; -import type { MCPProtocolVersion } from '../types/mcp.js'; -import { getLogger } from './logger.js'; -import { getCache } from './cache.js'; -import { ToolManager } from '../tools/manager.js'; -import { ResourceManager } from '../resources/manager.js'; -import { PromptManager } from '../prompts/manager.js'; -import { ValidationError, MCPError, ProtocolError } from '../utils/errors.js'; -import { z } from 'zod'; - -/** - * Production-ready MCP Server with comprehensive features - */ -export class MCPServer { - private server: McpServer; - private config: ServerConfig; - private logger = getLogger(); - private cache = getCache(); - private toolManager: ToolManager; - private resourceManager: ResourceManager; - private promptManager: PromptManager; - private isInitialized = false; - private clientConnections = new Map(); - - constructor(config: ServerConfig) { - this.config = config; - - // Initialize server with new API - this.server = new McpServer({ - name: config.name, - version: config.version, - }); - - // Initialize managers - this.toolManager = new ToolManager(); - this.resourceManager = new ResourceManager(); - this.promptManager = new PromptManager(); - - this.setupErrorHandling(); - } - - /** - * Set up all MCP protocol handlers using the new register* API - */ - private async setupHandlers(): Promise { - await this.setupToolHandlers(); - await this.setupResourceHandlers(); - await this.setupPromptHandlers(); - } - - /** - * Set up tool-related handlers - */ - private async setupToolHandlers(): Promise { - // Initialize the tool manager first - await this.toolManager.initialize(); - - // Get all tools from the tool manager - const tools = await this.toolManager.listTools(); - - for (const tool of tools) { - this.server.registerTool( - tool.name, - { - description: tool.description || `Tool: ${tool.name}`, - inputSchema: tool.inputSchema as any, - }, - async (args: any) => { - try { - const startTime = Date.now(); - const result = await this.toolManager.callTool(tool.name, args); - const duration = Date.now() - startTime; - - this.logger.performance(`tools/${tool.name}`, duration, { - toolName: tool.name, - success: !result.isError - }); - - return result; - } catch (error) { - this.logger.error(`Tool execution failed: ${tool.name}`, error); - throw new MCPError(-32603, 'Internal error executing tool'); - } - } - ); - } - } - - /** - * Set up resource-related handlers - */ - private async setupResourceHandlers(): Promise { - // Initialize the resource manager first - await this.resourceManager.initialize(); - - // Get all resources from the resource manager - const resources = await this.resourceManager.listResources(); - - for (const resource of resources) { - this.server.registerResource( - resource.name, - resource.uri, - { - description: resource.description || `Resource: ${resource.name}`, - mimeType: resource.mimeType, - }, - async (resourceUri) => { - try { - const startTime = Date.now(); - const result = await this.resourceManager.readResource(resourceUri.href); - const duration = Date.now() - startTime; - - this.logger.performance(`resources/${resource.name}`, duration, { - resourceName: resource.name, - uri: resourceUri.href - }); - - // Convert ResourceContents to the expected format - return { - contents: result.contents || [] - }; - } catch (error) { - this.logger.error(`Resource read failed: ${resource.name}`, error); - throw new MCPError(-32603, 'Internal error reading resource'); - } - } - ); - } - } - - /** - * Set up prompt-related handlers - */ - private async setupPromptHandlers(): Promise { - // Initialize the prompt manager first - await this.promptManager.initialize(); - - // Get all prompts from the prompt manager - const prompts = await this.promptManager.listPrompts(); - - for (const prompt of prompts) { - this.server.registerPrompt( - prompt.name, - { - description: prompt.description || `Prompt: ${prompt.name}`, - argsSchema: prompt.arguments as any, - }, - async (args: any) => { - try { - const startTime = Date.now(); - const result = await this.promptManager.getPrompt(prompt.name, args); - const duration = Date.now() - startTime; - - this.logger.performance(`prompts/${prompt.name}`, duration, { - promptName: prompt.name - }); - - return result; - } catch (error) { - this.logger.error(`Prompt execution failed: ${prompt.name}`, error); - throw new MCPError(-32603, 'Internal error executing prompt'); - } - } - ); - } - } - - /** - * Set up error handling - */ - private setupErrorHandling(): void { - // Handle uncaught errors - process.on('uncaughtException', (error) => { - this.logger.error('Uncaught exception', error); - process.exit(1); - }); - - process.on('unhandledRejection', (reason, promise) => { - this.logger.error('Unhandled rejection', { reason, promise }); - }); - } - - - - /** - * Connect to a transport and start the server - */ - async connect(transport: StdioServerTransport): Promise { - try { - this.logger.info('Connecting MCP server to transport'); - - // Set up handlers first - await this.setupHandlers(); - - await this.server.connect(transport); - this.isInitialized = true; - - const tools = await this.toolManager.listTools(); - const resources = await this.resourceManager.listResources(); - const prompts = await this.promptManager.listPrompts(); - - this.logger.info('MCP server connected successfully', { - name: this.config.name, - version: this.config.version, - toolCount: tools.length, - resourceCount: resources.length, - promptCount: prompts.length, - }); - - } catch (error) { - this.logger.error('Failed to connect MCP server', error); - throw error; - } - } - - /** - * Close the server and cleanup resources - */ - async close(): Promise { - try { - this.logger.info('Closing MCP server'); - - // Clear caches - this.cache.clear(); - - // Clear connections - this.clientConnections.clear(); - - this.isInitialized = false; - this.logger.info('MCP server closed successfully'); - - } catch (error) { - this.logger.error('Error closing MCP server', error); - throw error; - } - } - - /** - * Get server statistics - */ - async getStats(): Promise<{ - name: string; - version: string; - isInitialized: boolean; - toolCount: number; - resourceCount: number; - promptCount: number; - connectionCount: number; - uptime: number; - }> { - const tools = await this.toolManager.listTools(); - const resources = await this.resourceManager.listResources(); - const prompts = await this.promptManager.listPrompts(); - - return { - name: this.config.name, - version: this.config.version, - isInitialized: this.isInitialized, - toolCount: tools.length, - resourceCount: resources.length, - promptCount: prompts.length, - connectionCount: this.clientConnections.size, - uptime: process.uptime(), - }; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/index.ts b/mcp-ui-server-v2/src/index.ts deleted file mode 100644 index c481239..0000000 --- a/mcp-ui-server-v2/src/index.ts +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env node - -/** - * Dynamic Templates MCP Server v2.0 - * Production-ready MCP server for dynamic UI template generation - * - * Features: - * - 20+ fully dynamic template types - * - Comprehensive schema validation - * - Intelligent caching system - * - Performance monitoring - * - Resource and prompt management - * - Error handling and logging - */ - -import 'dotenv/config'; -import { MCPServer } from './core/server.js'; -import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; -import { createDefaultLogger, setDefaultLogger } from './core/logger.js'; -import { createDefaultCache, setDefaultCache } from './core/cache.js'; -import type { ServerConfig } from './types/mcp.js'; - -/** - * Create server configuration - */ -function createServerConfig(): ServerConfig { - return { - name: process.env.MCP_SERVER_NAME || 'dynamic-templates-mcp', - version: process.env.MCP_SERVER_VERSION || '2.0.0', - description: 'Production-ready MCP server for dynamic UI template generation', - author: 'Dynamic Templates Team', - license: 'MIT', - - capabilities: { - tools: { - listChanged: true - }, - resources: { - subscribe: true, - listChanged: true - }, - prompts: { - listChanged: true - }, - logging: { - level: (process.env.LOG_LEVEL as any) || 'info' - } - }, - - transport: { - type: 'stdio', - host: process.env.MCP_HOST, - port: process.env.MCP_PORT ? parseInt(process.env.MCP_PORT) : undefined, - path: process.env.MCP_PATH, - cors: { - origin: process.env.CORS_ORIGIN === 'true' ? true : process.env.CORS_ORIGIN?.split(',') || false, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization'], - credentials: process.env.CORS_CREDENTIALS === 'true' - } - } as any, - - logging: { - level: (process.env.LOG_LEVEL as 'debug' | 'info' | 'warn' | 'error') || 'info', - format: (process.env.LOG_FORMAT as 'json' | 'pretty') || (process.env.NODE_ENV === 'development' ? 'pretty' : 'json'), - destination: process.env.LOG_FILE - } as any, - - cache: { - enabled: process.env.CACHE_ENABLED !== 'false', - ttl: process.env.CACHE_TTL ? parseInt(process.env.CACHE_TTL) : 300, // 5 minutes - maxSize: process.env.CACHE_MAX_SIZE ? parseInt(process.env.CACHE_MAX_SIZE) : 1000, - strategy: 'lru' - }, - - security: { - rateLimit: { - enabled: process.env.RATE_LIMIT_ENABLED === 'true', - maxRequests: process.env.RATE_LIMIT_MAX ? parseInt(process.env.RATE_LIMIT_MAX) : 100, - windowMs: process.env.RATE_LIMIT_WINDOW ? parseInt(process.env.RATE_LIMIT_WINDOW) : 60000, // 1 minute - skipSuccessfulRequests: false - }, - auth: { - enabled: process.env.AUTH_ENABLED === 'true', - type: (process.env.AUTH_TYPE as 'apiKey' | 'jwt' | 'oauth2') || 'apiKey', - config: { - apiKey: process.env.API_KEY, - jwtSecret: process.env.JWT_SECRET, - oauth2Config: process.env.OAUTH2_CONFIG ? JSON.parse(process.env.OAUTH2_CONFIG) : undefined - } - }, - validation: { - enabled: process.env.VALIDATION_ENABLED !== 'false', - sanitizeInputs: process.env.SANITIZE_INPUTS !== 'false', - maxPayloadSize: process.env.MAX_PAYLOAD_SIZE ? parseInt(process.env.MAX_PAYLOAD_SIZE) : 1024 * 1024 // 1MB - } - } - }; -} - -/** - * Initialize the MCP server - */ -async function initialize(): Promise { - const config = createServerConfig(); - - // Initialize logger - const logger = createDefaultLogger(config.logging.level); - setDefaultLogger(logger); - - logger.info('Initializing Dynamic Templates MCP Server', { - name: config.name, - version: config.version, - nodeVersion: process.version, - platform: process.platform, - arch: process.arch, - pid: process.pid - }); - - // Initialize cache - const cache = createDefaultCache(); - setDefaultCache(cache); - - logger.info('Cache initialized', { - enabled: config.cache.enabled, - ttl: config.cache.ttl, - maxSize: config.cache.maxSize - }); - - // Create and initialize server - const server = new MCPServer(config); - - logger.info('MCP Server created', { - capabilities: config.capabilities, - transport: config.transport.type - }); - - return server; -} - -/** - * Main entry point - */ -async function main(): Promise { - try { - const server = await initialize(); - const transport = new StdioServerTransport(); - await server.connect(transport); - - // Keep the process alive - process.on('SIGINT', () => { - console.error('\nReceived SIGINT, shutting down gracefully...'); - server.close(); - }); - - process.on('SIGTERM', () => { - console.error('\nReceived SIGTERM, shutting down gracefully...'); - server.close(); - }); - - } catch (error) { - console.error('Failed to start MCP server:', error); - process.exit(1); - } -} - -/** - * Handle unhandled rejections and exceptions - */ -process.on('unhandledRejection', (reason, promise) => { - console.error('Unhandled Rejection at:', promise, 'reason:', reason); - process.exit(1); -}); - -process.on('uncaughtException', (error) => { - console.error('Uncaught Exception:', error); - process.exit(1); -}); - -// Start the server if this file is run directly -if (import.meta.url === `file://${process.argv[1]}`) { - main().catch((error) => { - console.error('Fatal error during startup:', error); - process.exit(1); - }); -} - -export { MCPServer, createServerConfig }; -export type { ServerConfig }; \ No newline at end of file diff --git a/mcp-ui-server-v2/src/prompts/manager.ts b/mcp-ui-server-v2/src/prompts/manager.ts deleted file mode 100644 index 7de6ef2..0000000 --- a/mcp-ui-server-v2/src/prompts/manager.ts +++ /dev/null @@ -1,324 +0,0 @@ -/** - * Prompt Manager for MCP Server - * Handles AI-assisted template design prompts - */ - -import type { Prompt, GetPromptResult, PromptMessage } from '../types/mcp.js'; -import { getLogger } from '../core/logger.js'; - -export class PromptManager { - private prompts = new Map(); - private logger = getLogger(); - - async initialize(): Promise { - try { - this.registerPrompts(); - - this.logger.info('Prompt Manager initialized', { - promptCount: this.prompts.size, - prompts: Array.from(this.prompts.keys()) - }); - } catch (error) { - this.logger.error('Failed to initialize Prompt Manager', error); - throw error; - } - } - - /** - * Register all available prompts - */ - private registerPrompts(): void { - this.registerPrompt({ - name: 'design_ui_template', - description: 'AI-assisted template design with requirements analysis', - arguments: [ - { - name: 'requirements', - description: 'User requirements for the UI template', - required: true - }, - { - name: 'context', - description: 'Additional context about the use case', - required: false - }, - { - name: 'templateType', - description: 'Preferred template type (optional)', - required: false - } - ], - handler: this.handleDesignUITemplate.bind(this) - }); - - this.registerPrompt({ - name: 'analyze_template_requirements', - description: 'Analyze user requirements and suggest optimal template configurations', - arguments: [ - { - name: 'description', - description: 'Description of what the user wants to build', - required: true - }, - { - name: 'industry', - description: 'Industry or domain context', - required: false - }, - { - name: 'audience', - description: 'Target audience for the template', - required: false - } - ], - handler: this.handleAnalyzeRequirements.bind(this) - }); - - this.registerPrompt({ - name: 'optimize_template_performance', - description: 'Provide suggestions for optimizing template performance', - arguments: [ - { - name: 'templateConfig', - description: 'Current template configuration', - required: true - }, - { - name: 'dataSize', - description: 'Expected data size (small/medium/large)', - required: false - } - ], - handler: this.handleOptimizePerformance.bind(this) - }); - } - - /** - * Register a new prompt - */ - registerPrompt(definition: PromptDefinition): void { - this.prompts.set(definition.name, definition); - this.logger.debug('Prompt registered', { promptName: definition.name }); - } - - /** - * List all available prompts - */ - async listPrompts(): Promise { - return Array.from(this.prompts.values()).map(def => ({ - name: def.name, - description: def.description, - arguments: def.arguments - })); - } - - /** - * Get a prompt with the given arguments - */ - async getPrompt(name: string, args: Record): Promise { - const prompt = this.prompts.get(name); - if (!prompt) { - throw new Error(`Prompt not found: ${name}`); - } - - try { - return await prompt.handler(args); - } catch (error) { - this.logger.error(`Prompt execution failed: ${name}`, error, { args }); - throw error; - } - } - - // Prompt Handlers - - private async handleDesignUITemplate(args: Record): Promise { - const { requirements, context, templateType } = args as { - requirements: string; - context?: string; - templateType?: string; - }; - - const messages: PromptMessage[] = [ - { - role: 'system', - content: { - type: 'text', - text: `You are an expert UI/UX designer specializing in creating dynamic templates. Your goal is to analyze user requirements and provide detailed specifications for template generation. - -You have access to these template types: -- dashboard: Business analytics dashboards with metrics and charts -- dataTable: Sortable, filterable tables with pagination -- productCatalog: E-commerce product listings with search and filters -- form: Multi-section forms with validation and various input types -- analytics: Comprehensive analytics dashboards with KPIs -- gallery: Image galleries with lightbox and categorization -- calendar: Event management and scheduling interfaces -- kanban: Task management boards with drag-and-drop -- chart: Data visualization with multiple chart types -- feed: Activity feeds and social media layouts -- stats: KPI displays with progress indicators -- timeline: Event timelines with media support -- profileCard: User profile displays -- pricing: Pricing plans and comparison tables -- wizard: Multi-step forms and processes -- map: Interactive maps with markers -- marketplace: Multi-vendor marketplaces -- ecommerce: Shopping interfaces -- blog: Blog layouts with posts -- portfolio: Project showcases - -Your task is to: -1. Analyze the user requirements -2. Suggest the most appropriate template type -3. Provide specific configuration recommendations -4. Include sample data structure if relevant -5. Suggest UI/UX best practices - -Be specific and actionable in your recommendations.` - } - }, - { - role: 'user', - content: { - type: 'text', - text: `Please help me design a UI template based on these requirements: - -Requirements: ${requirements} - -${context ? `Additional Context: ${context}` : ''} - -${templateType ? `Preferred Template Type: ${templateType}` : ''} - -Please provide: -1. Recommended template type and why -2. Specific configuration details -3. Sample data structure -4. UI/UX recommendations -5. Performance considerations` - } - } - ]; - - return { - description: 'AI-assisted template design based on user requirements', - messages - }; - } - - private async handleAnalyzeRequirements(args: Record): Promise { - const { description, industry, audience } = args as { - description: string; - industry?: string; - audience?: string; - }; - - const messages: PromptMessage[] = [ - { - role: 'system', - content: { - type: 'text', - text: `You are a requirements analyst specializing in UI/UX design. Your role is to analyze user descriptions and break them down into structured requirements for template generation. - -Focus on: -1. Identifying the core functionality needed -2. Understanding the user's goals and constraints -3. Determining data requirements -4. Suggesting appropriate template types -5. Identifying potential challenges and solutions - -Provide structured, actionable analysis that can guide template selection and configuration.` - } - }, - { - role: 'user', - content: { - type: 'text', - text: `Please analyze these requirements and provide structured recommendations: - -Description: ${description} - -${industry ? `Industry: ${industry}` : ''} - -${audience ? `Target Audience: ${audience}` : ''} - -Please provide: -1. Core functionality analysis -2. Data requirements -3. Template type recommendations (ranked) -4. Key features needed -5. Potential challenges and solutions -6. Success metrics to consider` - } - } - ]; - - return { - description: 'Requirements analysis for optimal template design', - messages - }; - } - - private async handleOptimizePerformance(args: Record): Promise { - const { templateConfig, dataSize } = args as { - templateConfig: string; - dataSize?: string; - }; - - const messages: PromptMessage[] = [ - { - role: 'system', - content: { - type: 'text', - text: `You are a performance optimization expert for UI templates. Your role is to analyze template configurations and provide specific recommendations for improving performance, scalability, and user experience. - -Consider: -1. Data loading and pagination strategies -2. Caching opportunities -3. Rendering optimizations -4. Mobile performance -5. Accessibility improvements -6. Code splitting possibilities - -Provide specific, actionable recommendations with reasoning.` - } - }, - { - role: 'user', - content: { - type: 'text', - text: `Please analyze this template configuration and provide performance optimization recommendations: - -Template Configuration: -${templateConfig} - -${dataSize ? `Expected Data Size: ${dataSize}` : ''} - -Please provide: -1. Performance bottleneck analysis -2. Specific optimization recommendations -3. Caching strategies -4. Mobile optimization tips -5. Accessibility improvements -6. Scalability considerations -7. Implementation priorities` - } - } - ]; - - return { - description: 'Performance optimization analysis for template configuration', - messages - }; - } -} - -interface PromptDefinition { - name: string; - description: string; - arguments: Array<{ - name: string; - description: string; - required: boolean; - }>; - handler: (args: Record) => Promise; -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/resources/manager.ts b/mcp-ui-server-v2/src/resources/manager.ts deleted file mode 100644 index 9799f7c..0000000 --- a/mcp-ui-server-v2/src/resources/manager.ts +++ /dev/null @@ -1,412 +0,0 @@ -/** - * Resource Manager for MCP Server - * Handles template documentation, schemas, and other resources - */ - -import { readFile } from 'fs/promises'; -import { join } from 'path'; -import type { Resource, ResourceContents } from '../types/mcp.js'; -import { getLogger } from '../core/logger.js'; -import { ResourceError } from '../utils/errors.js'; - -export class ResourceManager { - private resources = new Map(); - private logger = getLogger(); - private subscriptions = new Map>(); - - async initialize(): Promise { - try { - await this.registerResources(); - - this.logger.info('Resource Manager initialized', { - resourceCount: this.resources.size, - resources: Array.from(this.resources.keys()) - }); - } catch (error) { - this.logger.error('Failed to initialize Resource Manager', error); - throw error; - } - } - - /** - * Register all available resources - */ - private async registerResources(): Promise { - // Template documentation - this.registerResource({ - uri: 'template://docs', - name: 'Template Documentation', - description: 'Complete documentation for all available template types', - mimeType: 'text/markdown' - }); - - // Template schemas for each type - const templateTypes = [ - 'dashboard', 'dataTable', 'productCatalog', 'profileCard', 'timeline', - 'gallery', 'pricing', 'stats', 'calendar', 'wizard', 'chart', 'map', - 'kanban', 'feed', 'form', 'marketplace', 'analytics', 'ecommerce', - 'blog', 'portfolio' - ]; - - for (const type of templateTypes) { - this.registerResource({ - uri: `template://schema/${type}`, - name: `${type} Template Schema`, - description: `JSON schema for ${type} template type`, - mimeType: 'application/json' - }); - - this.registerResource({ - uri: `template://examples/${type}`, - name: `${type} Template Examples`, - description: `Example configurations for ${type} template`, - mimeType: 'application/json' - }); - } - - // Server information - this.registerResource({ - uri: 'server://info', - name: 'Server Information', - description: 'MCP server capabilities and configuration', - mimeType: 'application/json' - }); - - this.registerResource({ - uri: 'server://stats', - name: 'Server Statistics', - description: 'Performance and usage statistics', - mimeType: 'application/json' - }); - } - - /** - * Register a new resource - */ - registerResource(resource: Resource): void { - this.resources.set(resource.uri, resource); - this.logger.debug('Resource registered', { uri: resource.uri }); - } - - /** - * List all available resources - */ - async listResources(): Promise { - return Array.from(this.resources.values()); - } - - /** - * Read a resource by URI - */ - async readResource(uri: string): Promise { - const resource = this.resources.get(uri); - if (!resource) { - throw new ResourceError(uri, 'Resource not found'); - } - - try { - const content = await this.getResourceContent(uri); - - return { - uri, - mimeType: resource.mimeType || 'text/plain', - content: [{ - type: 'text', - text: content - }] - }; - } catch (error) { - this.logger.error(`Failed to read resource: ${uri}`, error); - throw new ResourceError(uri, error instanceof Error ? error.message : 'Unknown error'); - } - } - - /** - * Subscribe to resource changes - */ - async subscribe(uri: string): Promise { - if (!this.resources.has(uri)) { - throw new ResourceError(uri, 'Resource not found'); - } - - const clientId = 'default'; // In a real implementation, this would be per-client - - if (!this.subscriptions.has(uri)) { - this.subscriptions.set(uri, new Set()); - } - - this.subscriptions.get(uri)!.add(clientId); - this.logger.debug('Resource subscription added', { uri, clientId }); - } - - /** - * Unsubscribe from resource changes - */ - async unsubscribe(uri: string): Promise { - const clientId = 'default'; - - const subscribers = this.subscriptions.get(uri); - if (subscribers) { - subscribers.delete(clientId); - - if (subscribers.size === 0) { - this.subscriptions.delete(uri); - } - } - - this.logger.debug('Resource subscription removed', { uri, clientId }); - } - - /** - * Cleanup resources and subscriptions - */ - async cleanup(): Promise { - this.subscriptions.clear(); - this.logger.info('Resource Manager cleaned up'); - } - - /** - * Get the actual content for a resource - */ - private async getResourceContent(uri: string): Promise { - if (uri === 'template://docs') { - return this.getTemplateDocumentation(); - } - - if (uri.startsWith('template://schema/')) { - const templateType = uri.replace('template://schema/', ''); - return this.getTemplateSchema(templateType); - } - - if (uri.startsWith('template://examples/')) { - const templateType = uri.replace('template://examples/', ''); - return this.getTemplateExamples(templateType); - } - - if (uri === 'server://info') { - return this.getServerInfo(); - } - - if (uri === 'server://stats') { - return this.getServerStats(); - } - - throw new ResourceError(uri, 'Unknown resource type'); - } - - /** - * Generate template documentation - */ - private getTemplateDocumentation(): string { - return `# Dynamic Template System Documentation - -## Overview - -The Dynamic Template System provides 20+ template types for generating rich, interactive UI components. Each template is fully customizable with dynamic data, custom styling, and interactive features. - -## Available Template Types - -### Core Templates -- **Dashboard**: Business analytics dashboards with metrics and charts -- **Form**: Multi-section forms with validation and various input types -- **DataTable**: Sortable, filterable tables with pagination -- **ProductCatalog**: E-commerce product listings with search and filters -- **Gallery**: Image galleries with lightbox and categorization -- **Analytics**: Comprehensive analytics dashboards with KPIs - -### Specialized Templates -- **Calendar**: Event management and scheduling interfaces -- **Kanban**: Task management boards with drag-and-drop -- **Chart**: Data visualization with multiple chart types -- **Feed**: Activity feeds and social media layouts -- **Stats**: KPI displays with progress indicators -- **Timeline**: Event timelines with media support -- **ProfileCard**: User profile displays -- **Pricing**: Pricing plans and comparison tables -- **Wizard**: Multi-step forms and processes -- **Map**: Interactive maps with markers -- **Marketplace**: Multi-vendor marketplaces -- **Ecommerce**: Shopping interfaces -- **Blog**: Blog layouts with posts -- **Portfolio**: Project showcases - -## Dynamic Features - -### Customization Options -- **Theme Support**: Light, dark, and system themes -- **Color Schemes**: Custom primary colors and branding -- **Layout Options**: Multiple layout configurations per template -- **Data Binding**: Dynamic data from external sources -- **Interactive Elements**: Click handlers and form submissions - -### Use Case Adaptation -Templates automatically adapt based on provided use cases: -- Sales dashboards include revenue metrics -- DevOps dashboards focus on system monitoring -- Marketing templates emphasize campaign performance - -### Advanced Features -- **Caching**: Intelligent caching for performance -- **Validation**: Schema validation for all configurations -- **Error Handling**: Comprehensive error management -- **Logging**: Detailed operation logging - -## Usage Examples - -### Basic Template Generation -\`\`\`json -{ - "templateType": "dashboard", - "title": "Sales Dashboard", - "useCase": "sales tracking", - "theme": "light" -} -\`\`\` - -### Advanced Customization -\`\`\`json -{ - "templateType": "dashboard", - "title": "Custom Dashboard", - "customData": { - "metrics": [...], - "charts": [...], - "navigation": {...} - }, - "brandingConfig": { - "brandName": "Acme Corp", - "brandColors": ["#007bff", "#28a745"] - } -} -\`\`\` - -## Best Practices - -1. **Use Specific Use Cases**: Provide detailed use case descriptions for better template adaptation -2. **Leverage Custom Data**: Use customData for specific requirements -3. **Consider Performance**: Large datasets should use pagination -4. **Test Validation**: Always validate template configurations -5. **Monitor Caching**: Use cache statistics to optimize performance - -## Support - -For detailed schema information, see template://schema/{type} resources. -For examples, see template://examples/{type} resources. -`; - } - - /** - * Get schema for a specific template type - */ - private getTemplateSchema(templateType: string): string { - // This would typically load from the actual generator - return JSON.stringify({ - templateType, - schema: { - type: 'object', - properties: { - templateType: { type: 'string', const: templateType }, - title: { type: 'string' }, - description: { type: 'string' }, - theme: { type: 'string', enum: ['light', 'dark', 'system'] }, - // ... other properties would be specific to each template type - }, - required: ['templateType', 'title'] - }, - description: `Schema for ${templateType} template type`, - lastUpdated: new Date().toISOString() - }, null, 2); - } - - /** - * Get examples for a specific template type - */ - private getTemplateExamples(templateType: string): string { - // This would typically load examples from the generator - const examples = [ - { - name: `Basic ${templateType}`, - config: { - templateType, - title: `Sample ${templateType}`, - description: `A basic ${templateType} example`, - theme: 'system' - } - }, - { - name: `Advanced ${templateType}`, - config: { - templateType, - title: `Advanced ${templateType}`, - description: `An advanced ${templateType} with custom features`, - theme: 'dark', - customData: {} - } - } - ]; - - return JSON.stringify({ - templateType, - examples, - lastUpdated: new Date().toISOString() - }, null, 2); - } - - /** - * Get server information - */ - private getServerInfo(): string { - return JSON.stringify({ - name: 'Dynamic Templates MCP Server', - version: '2.0.0', - description: 'Production-ready MCP server for dynamic UI template generation', - capabilities: { - tools: ['generate_template', 'list_template_types', 'validate_template'], - resources: ['documentation', 'schemas', 'examples'], - prompts: ['design_ui_template'] - }, - templateTypes: [ - 'dashboard', 'dataTable', 'productCatalog', 'profileCard', 'timeline', - 'gallery', 'pricing', 'stats', 'calendar', 'wizard', 'chart', 'map', - 'kanban', 'feed', 'form', 'marketplace', 'analytics', 'ecommerce', - 'blog', 'portfolio' - ], - features: [ - 'Dynamic template generation', - 'Use case adaptation', - 'Custom data binding', - 'Theme support', - 'Caching', - 'Validation', - 'Error handling' - ], - uptime: process.uptime(), - lastStarted: new Date().toISOString() - }, null, 2); - } - - /** - * Get server statistics - */ - private getServerStats(): string { - return JSON.stringify({ - server: { - uptime: process.uptime(), - memoryUsage: process.memoryUsage(), - nodeVersion: process.version - }, - templates: { - availableTypes: 20, - // These would come from the template engine - generatedCount: 0, - cacheHitRate: 0, - averageGenerationTime: 0 - }, - resources: { - registered: this.resources.size, - subscriptions: this.subscriptions.size - }, - timestamp: new Date().toISOString() - }, null, 2); - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/simple-server.ts b/mcp-ui-server-v2/src/simple-server.ts new file mode 100644 index 0000000..98c0bf4 --- /dev/null +++ b/mcp-ui-server-v2/src/simple-server.ts @@ -0,0 +1,353 @@ +#!/usr/bin/env node + +/** + * Simplified Dynamic Templates MCP Server v2.0 + * Compatible with MCP SDK v1.17.0 + */ + +import 'dotenv/config'; +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { z } from 'zod'; + +// Template types available +const TEMPLATE_TYPES = [ + 'dashboard', 'form', 'table', 'analytics', 'ecommerce', 'blog', 'portfolio', + 'calendar', 'kanban', 'gallery', 'pricing', 'stats', 'timeline', 'wizard', + 'chart', 'map', 'feed', 'profile-card', 'data-table', 'marketplace' +]; + +// Create the MCP server +const server = new McpServer({ + name: process.env.MCP_SERVER_NAME || 'dynamic-templates-mcp', + version: process.env.MCP_SERVER_VERSION || '2.0.0', +}); + +/** + * Generate a basic template configuration + */ +function generateTemplateConfig(templateType: string, options: any = {}) { + const baseConfig = { + id: `template_${Date.now()}`, + type: templateType, + title: options.title || `${templateType.charAt(0).toUpperCase() + templateType.slice(1)} Template`, + description: options.description || `A dynamic ${templateType} template`, + config: { + theme: options.theme || 'modern', + primaryColor: options.primaryColor || '#3b82f6', + layout: options.layout || 'default', + responsive: true, + darkMode: options.darkMode !== false, + }, + data: generateSampleData(templateType), + metadata: { + category: getCategoryForTemplate(templateType), + complexity: options.complexity || 'medium', + tags: [templateType, options.theme || 'modern'], + createdAt: new Date().toISOString(), + } + }; + + return baseConfig; +} + +/** + * Generate sample data for different template types + */ +function generateSampleData(templateType: string) { + switch (templateType) { + case 'dashboard': + return { + metrics: [ + { label: 'Total Users', value: '12,543', change: '+12%' }, + { label: 'Revenue', value: '$45,678', change: '+8%' }, + { label: 'Orders', value: '1,234', change: '+15%' }, + { label: 'Conversion', value: '3.2%', change: '+0.5%' } + ], + charts: [ + { type: 'line', title: 'Revenue Trend', data: [100, 120, 140, 130, 160, 180] }, + { type: 'bar', title: 'Sales by Category', data: [45, 67, 89, 56, 78] } + ] + }; + + case 'form': + return { + fields: [ + { name: 'name', type: 'text', label: 'Full Name', required: true }, + { name: 'email', type: 'email', label: 'Email Address', required: true }, + { name: 'phone', type: 'tel', label: 'Phone Number', required: false }, + { name: 'message', type: 'textarea', label: 'Message', required: true } + ], + submitButton: { text: 'Submit', style: 'primary' }, + validation: { enabled: true, showErrors: true } + }; + + case 'table': + return { + columns: [ + { key: 'id', label: 'ID', sortable: true }, + { key: 'name', label: 'Name', sortable: true }, + { key: 'email', label: 'Email', sortable: true }, + { key: 'status', label: 'Status', sortable: false }, + { key: 'actions', label: 'Actions', sortable: false } + ], + rows: [ + { id: 1, name: 'John Doe', email: 'john@example.com', status: 'Active' }, + { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'Inactive' }, + { id: 3, name: 'Bob Johnson', email: 'bob@example.com', status: 'Active' } + ], + pagination: { enabled: true, pageSize: 10 } + }; + + case 'ecommerce': + return { + products: [ + { id: 1, name: 'Wireless Headphones', price: 99.99, image: '/api/placeholder/200/200', rating: 4.5 }, + { id: 2, name: 'Smart Watch', price: 299.99, image: '/api/placeholder/200/200', rating: 4.8 }, + { id: 3, name: 'Laptop Stand', price: 49.99, image: '/api/placeholder/200/200', rating: 4.2 } + ], + categories: ['Electronics', 'Accessories', 'Gadgets'], + filters: ['Price', 'Brand', 'Rating', 'Availability'] + }; + + default: + return { + items: [ + { id: 1, title: 'Sample Item 1', description: 'This is a sample item for the template' }, + { id: 2, title: 'Sample Item 2', description: 'Another sample item for demonstration' }, + { id: 3, title: 'Sample Item 3', description: 'A third sample item to show variety' } + ] + }; + } +} + +/** + * Get category for template type + */ +function getCategoryForTemplate(templateType: string): string { + const categories: Record = { + dashboard: 'analytics', + form: 'input', + table: 'data', + analytics: 'analytics', + ecommerce: 'commerce', + blog: 'content', + portfolio: 'showcase', + calendar: 'scheduling', + kanban: 'productivity', + gallery: 'media', + pricing: 'commerce', + stats: 'analytics', + timeline: 'display', + wizard: 'input', + chart: 'analytics', + map: 'location', + feed: 'content', + 'profile-card': 'social', + 'data-table': 'data', + marketplace: 'commerce' + }; + + return categories[templateType] || 'general'; +} + +// Register the main template generation tool +server.registerTool( + 'generate_template', + { + description: 'Generate a dynamic UI template with customization options', + inputSchema: { + templateType: z.enum(TEMPLATE_TYPES as [string, ...string[]]).describe('Type of template to generate'), + title: z.string().optional().describe('Title for the template'), + description: z.string().optional().describe('Description of the template'), + theme: z.enum(['modern', 'classic', 'minimal', 'dark']).optional().describe('Visual theme'), + primaryColor: z.string().optional().describe('Primary color (hex code)'), + complexity: z.enum(['simple', 'medium', 'complex']).optional().describe('Template complexity level'), + layout: z.string().optional().describe('Layout style'), + darkMode: z.boolean().optional().describe('Enable dark mode support'), + }, + }, + async ({ templateType, title, description, theme, primaryColor, complexity, layout, darkMode }) => { + try { + const templateConfig = generateTemplateConfig(templateType, { + title, + description, + theme, + primaryColor, + complexity, + layout, + darkMode + }); + + return { + content: [{ + type: 'text', + text: JSON.stringify(templateConfig, null, 2) + }] + }; + } catch (error) { + return { + content: [{ + type: 'text', + text: JSON.stringify({ + error: 'Failed to generate template', + message: error instanceof Error ? error.message : 'Unknown error' + }, null, 2) + }], + isError: true + }; + } + } +); + +// Register template types listing tool +server.registerTool( + 'list_template_types', + { + description: 'List all available template types and their descriptions', + inputSchema: {}, + }, + async () => { + const templateInfo = TEMPLATE_TYPES.map(type => ({ + type, + category: getCategoryForTemplate(type), + description: `Generate a ${type} template with dynamic content and styling` + })); + + return { + content: [{ + type: 'text', + text: JSON.stringify({ + templateTypes: templateInfo, + total: TEMPLATE_TYPES.length + }, null, 2) + }] + }; + } +); + +// Register template suggestions tool +server.registerTool( + 'get_template_suggestions', + { + description: 'Get template suggestions based on user requirements', + inputSchema: { + useCase: z.string().describe('Description of the use case or requirements'), + category: z.enum(['analytics', 'input', 'data', 'commerce', 'content', 'showcase', 'scheduling', 'productivity', 'media', 'location', 'social', 'general']).optional().describe('Preferred category'), + }, + }, + async ({ useCase, category }) => { + // Simple keyword-based suggestions + const suggestions = []; + const lowerUseCase = useCase.toLowerCase(); + + if (lowerUseCase.includes('dashboard') || lowerUseCase.includes('metric') || lowerUseCase.includes('kpi')) { + suggestions.push('dashboard', 'analytics', 'stats'); + } + if (lowerUseCase.includes('form') || lowerUseCase.includes('input') || lowerUseCase.includes('contact')) { + suggestions.push('form', 'wizard'); + } + if (lowerUseCase.includes('table') || lowerUseCase.includes('data') || lowerUseCase.includes('list')) { + suggestions.push('table', 'data-table'); + } + if (lowerUseCase.includes('shop') || lowerUseCase.includes('product') || lowerUseCase.includes('ecommerce')) { + suggestions.push('ecommerce', 'marketplace', 'pricing'); + } + if (lowerUseCase.includes('blog') || lowerUseCase.includes('article') || lowerUseCase.includes('content')) { + suggestions.push('blog', 'feed'); + } + if (lowerUseCase.includes('portfolio') || lowerUseCase.includes('showcase') || lowerUseCase.includes('gallery')) { + suggestions.push('portfolio', 'gallery'); + } + + // Filter by category if specified + if (category && suggestions.length === 0) { + const categoryTemplates = TEMPLATE_TYPES.filter(type => getCategoryForTemplate(type) === category); + suggestions.push(...categoryTemplates.slice(0, 3)); + } + + // Default suggestions if none found + if (suggestions.length === 0) { + suggestions.push('dashboard', 'form', 'table'); + } + + return { + content: [{ + type: 'text', + text: JSON.stringify({ + useCase, + category, + suggestions: [...new Set(suggestions)].slice(0, 5), + allTypes: TEMPLATE_TYPES + }, null, 2) + }] + }; + } +); + +// Register a simple resource for server info +server.registerResource( + 'server-info', + 'mcp://dynamic-templates/server-info', + { + description: 'Information about the MCP server capabilities', + mimeType: 'application/json', + }, + async () => { + return { + contents: [{ + uri: 'mcp://dynamic-templates/server-info', + mimeType: 'application/json', + text: JSON.stringify({ + name: 'Dynamic Templates MCP Server', + version: '2.0.0', + capabilities: { + templateGeneration: true, + templateTypes: TEMPLATE_TYPES.length, + supportedThemes: ['modern', 'classic', 'minimal', 'dark'], + supportedComplexity: ['simple', 'medium', 'complex'] + }, + availableTemplates: TEMPLATE_TYPES, + tools: ['generate_template', 'list_template_types', 'get_template_suggestions'] + }, null, 2) + }] + }; + } +); + +// Main function to start the server +async function main() { + try { + console.error('🚀 Starting Dynamic Templates MCP Server v2.0...'); + + const transport = new StdioServerTransport(); + await server.connect(transport); + + console.error('✅ MCP Server connected and ready!'); + console.error(`📋 Available tools: generate_template, list_template_types, get_template_suggestions`); + console.error(`🎨 Template types: ${TEMPLATE_TYPES.length} available`); + + } catch (error) { + console.error('❌ Failed to start MCP server:', error); + process.exit(1); + } +} + +// Handle process termination +process.on('SIGINT', () => { + console.error('\n🛑 Received SIGINT, shutting down gracefully...'); + process.exit(0); +}); + +process.on('SIGTERM', () => { + console.error('\n🛑 Received SIGTERM, shutting down gracefully...'); + process.exit(0); +}); + +// Start the server +if (import.meta.url === `file://${process.argv[1]}`) { + main().catch((error) => { + console.error('💥 Server startup failed:', error); + process.exit(1); + }); +} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/engine.ts b/mcp-ui-server-v2/src/templates/engine.ts deleted file mode 100644 index 058c8b6..0000000 --- a/mcp-ui-server-v2/src/templates/engine.ts +++ /dev/null @@ -1,428 +0,0 @@ -/** - * Template Engine - Core template generation system - * Provides dynamic template generation with full customization support - */ - -import { getLogger } from '../core/logger.js'; -import { TemplateError } from '../utils/errors.js'; -import type { TemplateGenerationParams, TemplateType } from '../types/mcp.js'; -import { DashboardGenerator } from './generators/dashboard.js'; -import { FormGenerator } from './generators/form.js'; -import { DataTableGenerator } from './generators/data-table.js'; -import { ProductCatalogGenerator } from './generators/product-catalog.js'; -import { GalleryGenerator } from './generators/gallery.js'; -import { AnalyticsGenerator } from './generators/analytics.js'; -import { CalendarGenerator } from './generators/calendar.js'; -import { KanbanGenerator } from './generators/kanban.js'; -import { ChartGenerator } from './generators/chart.js'; -import { FeedGenerator } from './generators/feed.js'; -import { StatsGenerator } from './generators/stats.js'; -import { TimelineGenerator } from './generators/timeline.js'; -import { ProfileCardGenerator } from './generators/profile-card.js'; -import { PricingGenerator } from './generators/pricing.js'; -import { WizardGenerator } from './generators/wizard.js'; -import { MapGenerator } from './generators/map.js'; -import { MarketplaceGenerator } from './generators/marketplace.js'; -import { EcommerceGenerator } from './generators/ecommerce.js'; -import { BlogGenerator } from './generators/blog.js'; -import { PortfolioGenerator } from './generators/portfolio.js'; -import { SampleDataGenerator } from './generators/sample-data.js'; - -export interface TemplateGenerator { - name: string; - description: string; - capabilities: string[]; - useCases: string[]; - schema: any; - - generate(params: TemplateGenerationParams): Promise; - validate(config: T): Promise; - getExamples(): Promise; -} - -export interface TemplateStats { - totalTemplates: number; - generatedCount: number; - cacheHitRate: number; - averageGenerationTime: number; - popularTemplates: Array<{ type: string; count: number }>; -} - -export class TemplateEngine { - private generators = new Map(); - private logger = getLogger(); - private stats: TemplateStats = { - totalTemplates: 0, - generatedCount: 0, - cacheHitRate: 0, - averageGenerationTime: 0, - popularTemplates: [] - }; - private generationTimes: number[] = []; - private sampleDataGenerator: SampleDataGenerator; - - constructor() { - this.sampleDataGenerator = new SampleDataGenerator(); - } - - async initialize(): Promise { - try { - // Register all template generators - await this.registerGenerators(); - - this.logger.info('Template Engine initialized', { - generatorCount: this.generators.size, - availableTypes: Array.from(this.generators.keys()) - }); - } catch (error) { - this.logger.error('Failed to initialize Template Engine', error); - throw error; - } - } - - /** - * Register all template generators - */ - private async registerGenerators(): Promise { - // Core generators with full dynamic support - this.registerGenerator('dashboard', new DashboardGenerator()); - this.registerGenerator('form', new FormGenerator()); - this.registerGenerator('dataTable', new DataTableGenerator()); - this.registerGenerator('productCatalog', new ProductCatalogGenerator()); - this.registerGenerator('gallery', new GalleryGenerator()); - this.registerGenerator('analytics', new AnalyticsGenerator()); - - // Enhanced generators - this.registerGenerator('calendar', new CalendarGenerator()); - this.registerGenerator('kanban', new KanbanGenerator()); - this.registerGenerator('chart', new ChartGenerator()); - this.registerGenerator('feed', new FeedGenerator()); - this.registerGenerator('stats', new StatsGenerator()); - this.registerGenerator('timeline', new TimelineGenerator()); - this.registerGenerator('profileCard', new ProfileCardGenerator()); - this.registerGenerator('pricing', new PricingGenerator()); - this.registerGenerator('wizard', new WizardGenerator()); - this.registerGenerator('map', new MapGenerator()); - this.registerGenerator('marketplace', new MarketplaceGenerator()); - this.registerGenerator('ecommerce', new EcommerceGenerator()); - this.registerGenerator('blog', new BlogGenerator()); - this.registerGenerator('portfolio', new PortfolioGenerator()); - - this.stats.totalTemplates = this.generators.size; - } - - /** - * Register a template generator - */ - private registerGenerator(type: TemplateType, generator: TemplateGenerator): void { - this.generators.set(type, generator); - this.logger.debug('Template generator registered', { - type, - name: generator.name, - capabilities: generator.capabilities.length - }); - } - - /** - * Generate a template with the given parameters - */ - async generateTemplate(params: TemplateGenerationParams): Promise { - const startTime = Date.now(); - - try { - const generator = this.generators.get(params.templateType); - if (!generator) { - throw new TemplateError(params.templateType, `Template type not found: ${params.templateType}`); - } - - this.logger.debug('Generating template', { - type: params.templateType, - title: params.title, - useCase: params.useCase - }); - - // Generate the template - const result = await generator.generate(params); - - // Update statistics - const duration = Date.now() - startTime; - this.updateStats(params.templateType, duration); - - this.logger.info('Template generated successfully', { - type: params.templateType, - title: params.title, - duration, - size: this.estimateSize(result) - }); - - return result; - } catch (error) { - const duration = Date.now() - startTime; - this.logger.error('Template generation failed', error, { - type: params.templateType, - title: params.title, - duration - }); - - if (error instanceof TemplateError) { - throw error; - } - - throw new TemplateError( - params.templateType, - error instanceof Error ? error.message : 'Unknown generation error' - ); - } - } - - /** - * Get available template types with their capabilities - */ - async getAvailableTemplates(): Promise> { - return Array.from(this.generators.entries()).map(([type, generator]) => ({ - type, - name: generator.name, - description: generator.description, - capabilities: generator.capabilities, - useCases: generator.useCases, - schema: generator.schema - })); - } - - /** - * Get schema for a specific template type - */ - async getTemplateSchema(templateType: string): Promise { - const generator = this.generators.get(templateType as TemplateType); - if (!generator) { - throw new TemplateError(templateType, `Template type not found: ${templateType}`); - } - - return { - templateType, - name: generator.name, - description: generator.description, - schema: generator.schema, - capabilities: generator.capabilities, - useCases: generator.useCases, - examples: await generator.getExamples().catch(() => []) - }; - } - - /** - * Validate a template configuration - */ - async validateTemplate(templateType: string, config: any): Promise { - const generator = this.generators.get(templateType as TemplateType); - if (!generator) { - throw new TemplateError(templateType, `Template type not found: ${templateType}`); - } - - try { - return await generator.validate(config); - } catch (error) { - this.logger.warn('Template validation failed', { - templateType, - error: error instanceof Error ? error.message : 'Unknown error' - }); - return false; - } - } - - /** - * Generate sample data - */ - async generateSampleData(dataType: string, count: number, seed?: string): Promise { - try { - return await this.sampleDataGenerator.generate(dataType, count, seed); - } catch (error) { - this.logger.error('Sample data generation failed', error, { dataType, count, seed }); - throw new TemplateError('sample-data', `Failed to generate ${dataType} data: ${error}`); - } - } - - /** - * Get template generation statistics - */ - async getStats(): Promise { - // Calculate average generation time - if (this.generationTimes.length > 0) { - this.stats.averageGenerationTime = - this.generationTimes.reduce((sum, time) => sum + time, 0) / this.generationTimes.length; - } - - return { ...this.stats }; - } - - /** - * Get examples for a template type - */ - async getTemplateExamples(templateType: string): Promise { - const generator = this.generators.get(templateType as TemplateType); - if (!generator) { - throw new TemplateError(templateType, `Template type not found: ${templateType}`); - } - - try { - return await generator.getExamples(); - } catch (error) { - this.logger.warn('Failed to get template examples', { - templateType, - error: error instanceof Error ? error.message : 'Unknown error' - }); - return []; - } - } - - /** - * Get generator capabilities - */ - getGeneratorCapabilities(templateType: string): string[] { - const generator = this.generators.get(templateType as TemplateType); - return generator ? generator.capabilities : []; - } - - /** - * Check if a template type is supported - */ - isTemplateSupported(templateType: string): boolean { - return this.generators.has(templateType as TemplateType); - } - - /** - * Get recommended templates based on use case - */ - getRecommendedTemplates(useCase: string): Array<{ - type: TemplateType; - name: string; - relevanceScore: number; - reason: string; - }> { - const recommendations: Array<{ - type: TemplateType; - name: string; - relevanceScore: number; - reason: string; - }> = []; - - const lowerUseCase = useCase.toLowerCase(); - - for (const [type, generator] of this.generators) { - let relevanceScore = 0; - let reason = ''; - - // Check use cases - for (const generatorUseCase of generator.useCases) { - if (generatorUseCase.toLowerCase().includes(lowerUseCase) || - lowerUseCase.includes(generatorUseCase.toLowerCase())) { - relevanceScore += 3; - reason = `Matches use case: ${generatorUseCase}`; - break; - } - } - - // Check capabilities - for (const capability of generator.capabilities) { - if (capability.toLowerCase().includes(lowerUseCase) || - lowerUseCase.includes(capability.toLowerCase())) { - relevanceScore += 2; - if (!reason) reason = `Supports capability: ${capability}`; - break; - } - } - - // Check template name and description - if (generator.name.toLowerCase().includes(lowerUseCase) || - generator.description.toLowerCase().includes(lowerUseCase)) { - relevanceScore += 1; - if (!reason) reason = `Related to template type`; - } - - if (relevanceScore > 0) { - recommendations.push({ - type, - name: generator.name, - relevanceScore, - reason - }); - } - } - - return recommendations.sort((a, b) => b.relevanceScore - a.relevanceScore); - } - - /** - * Update internal statistics - */ - private updateStats(templateType: TemplateType, duration: number): void { - this.stats.generatedCount++; - this.generationTimes.push(duration); - - // Keep only last 1000 generation times to prevent memory bloat - if (this.generationTimes.length > 1000) { - this.generationTimes = this.generationTimes.slice(-1000); - } - - // Update popular templates - const existing = this.stats.popularTemplates.find(t => t.type === templateType); - if (existing) { - existing.count++; - } else { - this.stats.popularTemplates.push({ type: templateType, count: 1 }); - } - - // Sort and keep top 10 - this.stats.popularTemplates.sort((a, b) => b.count - a.count); - if (this.stats.popularTemplates.length > 10) { - this.stats.popularTemplates = this.stats.popularTemplates.slice(0, 10); - } - } - - /** - * Estimate the size of a generated template - */ - private estimateSize(template: any): number { - try { - return JSON.stringify(template).length; - } catch { - return 0; - } - } - - /** - * Clear statistics - */ - clearStats(): void { - this.stats = { - totalTemplates: this.generators.size, - generatedCount: 0, - cacheHitRate: 0, - averageGenerationTime: 0, - popularTemplates: [] - }; - this.generationTimes = []; - - this.logger.info('Template engine statistics cleared'); - } - - /** - * Get generator for a specific template type - */ - getGenerator(templateType: TemplateType): TemplateGenerator | undefined { - return this.generators.get(templateType); - } - - /** - * Get all registered template types - */ - getRegisteredTypes(): TemplateType[] { - return Array.from(this.generators.keys()); - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/analytics.ts b/mcp-ui-server-v2/src/templates/generators/analytics.ts deleted file mode 100644 index 5b416d8..0000000 --- a/mcp-ui-server-v2/src/templates/generators/analytics.ts +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Analytics Generator - Analytics dashboards and metrics - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const AnalyticsConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - metrics: z.array(z.object({ - id: z.string(), - name: z.string(), - value: z.number(), - change: z.number().optional(), - trend: z.enum(['up', 'down', 'stable']).default('stable') - })).default([]), - charts: z.array(z.object({ - id: z.string(), - type: z.enum(['line', 'bar', 'pie', 'area']).default('line'), - title: z.string(), - data: z.array(z.any()).default([]) - })).default([]) -}); - -type AnalyticsConfig = z.infer; - -export class AnalyticsGenerator implements TemplateGenerator { - name = 'Analytics Generator'; - description = 'Generate analytics dashboards and metrics displays'; - capabilities = ['KPI metrics', 'Chart visualization', 'Trend analysis', 'Performance tracking']; - useCases = ['Business dashboards', 'Performance monitoring', 'Data visualization', 'Reporting']; - schema = AnalyticsConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'analytics-dashboard', - title: params.title || 'Analytics Dashboard', - description: 'Monitor your key performance indicators', - metrics: [ - { id: 'revenue', name: 'Revenue', value: 125000, change: 12.5, trend: 'up' }, - { id: 'users', name: 'Active Users', value: 2450, change: -3.2, trend: 'down' }, - { id: 'conversion', name: 'Conversion Rate', value: 3.8, change: 0.5, trend: 'up' } - ], - charts: [ - { id: 'revenue-chart', type: 'line', title: 'Revenue Trend', data: [] }, - { id: 'users-chart', type: 'bar', title: 'User Growth', data: [] } - ] - }; - } - - async validate(config: AnalyticsConfig): Promise { - try { - AnalyticsConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'analytics', title: 'Example Analytics' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/blog.ts b/mcp-ui-server-v2/src/templates/generators/blog.ts deleted file mode 100644 index 49b7eef..0000000 --- a/mcp-ui-server-v2/src/templates/generators/blog.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Blog Generator - Blog and content layouts - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const BlogConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional() -}); - -type BlogConfig = z.infer; - -export class BlogGenerator implements TemplateGenerator { - name = 'Blog Generator'; - description = 'Generate blog and content management layouts'; - capabilities = ['Article layouts', 'Content organization', 'Publishing tools']; - useCases = ['Personal blogs', 'Company blogs', 'News sites', 'Content platforms']; - schema = BlogConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'blog-layout', - title: params.title || 'Blog', - description: 'Share your thoughts and ideas' - }; - } - - async validate(config: BlogConfig): Promise { - try { - BlogConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'blog', title: 'Example Blog' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/calendar.ts b/mcp-ui-server-v2/src/templates/generators/calendar.ts deleted file mode 100644 index fc8512a..0000000 --- a/mcp-ui-server-v2/src/templates/generators/calendar.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Calendar Generator - Calendar and event scheduling - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const CalendarConfigSchema = z.object({ - id: z.string(), - title: z.string(), - view: z.enum(['month', 'week', 'day', 'agenda']).default('month'), - events: z.array(z.object({ - id: z.string(), - title: z.string(), - start: z.string(), - end: z.string(), - color: z.string().optional() - })).default([]) -}); - -type CalendarConfig = z.infer; - -export class CalendarGenerator implements TemplateGenerator { - name = 'Calendar Generator'; - description = 'Generate calendar and event scheduling interfaces'; - capabilities = ['Event scheduling', 'Multiple views', 'Event management']; - useCases = ['Event calendars', 'Scheduling', 'Meeting planning']; - schema = CalendarConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'calendar', - title: params.title || 'Calendar', - view: 'month', - events: [ - { id: '1', title: 'Team Meeting', start: '2024-01-15T10:00:00', end: '2024-01-15T11:00:00', color: '#3b82f6' }, - { id: '2', title: 'Project Review', start: '2024-01-16T14:00:00', end: '2024-01-16T15:30:00', color: '#10b981' } - ] - }; - } - - async validate(config: CalendarConfig): Promise { - try { - CalendarConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'calendar', title: 'Example Calendar' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/chart.ts b/mcp-ui-server-v2/src/templates/generators/chart.ts deleted file mode 100644 index 9afd62f..0000000 --- a/mcp-ui-server-v2/src/templates/generators/chart.ts +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Chart Generator - Charts and data visualization - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const ChartConfigSchema = z.object({ - id: z.string(), - title: z.string(), - type: z.enum(['line', 'bar', 'pie', 'area', 'scatter']).default('line'), - data: z.object({ - labels: z.array(z.string()).default([]), - datasets: z.array(z.object({ - label: z.string(), - data: z.array(z.number()), - color: z.string().optional() - })).default([]) - }), - options: z.object({ - responsive: z.boolean().default(true), - legend: z.boolean().default(true), - grid: z.boolean().default(true) - }).default({}) -}); - -type ChartConfig = z.infer; - -export class ChartGenerator implements TemplateGenerator { - name = 'Chart Generator'; - description = 'Generate charts and data visualization components'; - capabilities = ['Multiple chart types', 'Data visualization', 'Interactive charts']; - useCases = ['Data analysis', 'Reporting', 'Dashboard widgets', 'Performance metrics']; - schema = ChartConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'data-chart', - title: params.title || 'Data Chart', - type: 'line', - data: { - labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], - datasets: [ - { - label: 'Revenue', - data: [12000, 15000, 18000, 16000, 22000, 25000], - color: '#3b82f6' - }, - { - label: 'Expenses', - data: [8000, 9000, 11000, 12000, 13000, 14000], - color: '#ef4444' - } - ] - }, - options: { - responsive: true, - legend: true, - grid: true - } - }; - } - - async validate(config: ChartConfig): Promise { - try { - ChartConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'chart', title: 'Example Chart' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/dashboard.ts b/mcp-ui-server-v2/src/templates/generators/dashboard.ts deleted file mode 100644 index 83b756b..0000000 --- a/mcp-ui-server-v2/src/templates/generators/dashboard.ts +++ /dev/null @@ -1,733 +0,0 @@ -/** - * Dashboard Template Generator - * Generates comprehensive dashboard templates with metrics, charts, and activity feeds - */ - -import { z } from 'zod'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; -import type { TemplateGenerator } from '../engine.js'; -import { getLogger } from '../../core/logger.js'; - -// Dashboard-specific schema -const DashboardSchema = z.object({ - templateType: z.literal('dashboard'), - title: z.string(), - description: z.string().optional(), - theme: z.enum(['light', 'dark', 'system']).default('system'), - primaryColor: z.string().optional(), - fullScreen: z.boolean().default(false), - - // Dashboard-specific fields - layout: z.enum(['grid', 'columns', 'rows']).default('grid'), - metrics: z.array(z.object({ - id: z.string(), - label: z.string(), - value: z.union([z.string(), z.number()]), - change: z.number().optional(), - changeType: z.enum(['increase', 'decrease', 'neutral']).optional(), - icon: z.string().optional(), - color: z.string().optional(), - description: z.string().optional(), - target: z.number().optional(), - unit: z.string().optional(), - trendData: z.array(z.number()).optional() - })), - - charts: z.array(z.object({ - id: z.string(), - type: z.enum(['line', 'bar', 'pie', 'area', 'scatter', 'radar']), - title: z.string(), - description: z.string().optional(), - data: z.object({ - labels: z.array(z.string()), - datasets: z.array(z.object({ - label: z.string(), - data: z.array(z.number()), - backgroundColor: z.union([z.string(), z.array(z.string())]).optional(), - borderColor: z.string().optional(), - fill: z.boolean().optional() - })) - }), - options: z.record(z.any()).optional() - })), - - recentActivity: z.array(z.object({ - id: z.string(), - type: z.string(), - title: z.string(), - description: z.string(), - timestamp: z.string(), - user: z.object({ - name: z.string(), - avatar: z.string().optional() - }).optional(), - status: z.enum(['success', 'warning', 'error', 'info']).optional(), - icon: z.string().optional() - })).optional(), - - widgets: z.array(z.object({ - id: z.string(), - type: z.string(), - title: z.string(), - size: z.enum(['small', 'medium', 'large']).default('medium'), - position: z.object({ - x: z.number(), - y: z.number(), - width: z.number(), - height: z.number() - }).optional(), - config: z.record(z.any()).optional() - })).optional(), - - navigation: z.object({ - items: z.array(z.object({ - id: z.string(), - label: z.string(), - icon: z.string().optional(), - href: z.string().optional(), - active: z.boolean().default(false) - })), - showSearch: z.boolean().default(true), - showNotifications: z.boolean().default(true), - showProfile: z.boolean().default(true) - }).optional(), - - filters: z.object({ - timeRange: z.object({ - options: z.array(z.string()), - default: z.string() - }).optional(), - categories: z.array(z.string()).optional(), - customFilters: z.array(z.object({ - id: z.string(), - label: z.string(), - type: z.enum(['select', 'multi-select', 'date', 'range']), - options: z.array(z.string()).optional() - })).optional() - }).optional() -}); - -type DashboardConfig = z.infer; - -export class DashboardGenerator implements TemplateGenerator { - name = 'Dynamic Dashboard Generator'; - description = 'Creates comprehensive dashboards with real-time metrics, interactive charts, and activity feeds'; - capabilities = [ - 'Real-time metrics display', - 'Multiple chart types (line, bar, pie, area, scatter, radar)', - 'Customizable layouts (grid, columns, rows)', - 'Recent activity feeds with user context', - 'Interactive widgets with drag-and-drop support', - 'Advanced filtering and time range selection', - 'Responsive design with mobile optimization', - 'Custom color schemes and theming', - 'Dynamic data binding and updates', - 'Export and sharing capabilities' - ]; - useCases = [ - 'Business analytics dashboards', - 'Application monitoring and DevOps', - 'Financial reporting and KPI tracking', - 'Project management overviews', - 'Marketing campaign performance', - 'Sales performance monitoring', - 'Customer success dashboards', - 'Operational efficiency tracking', - 'E-commerce analytics', - 'Social media monitoring' - ]; - - schema = DashboardSchema; - private logger = getLogger(); - - async generate(params: TemplateGenerationParams): Promise { - this.logger.debug('Generating dashboard template', { - title: params.title, - useCase: params.useCase - }); - - // Generate base configuration - const config: DashboardConfig = { - templateType: 'dashboard', - title: params.title, - description: params.description || `${params.title} - Comprehensive dashboard with real-time insights`, - theme: params.theme || 'system', - primaryColor: params.primaryColor, - fullScreen: params.fullScreen || false, - layout: this.determineLayout(params), - metrics: await this.generateMetrics(params), - charts: await this.generateCharts(params), - recentActivity: await this.generateActivity(params), - widgets: await this.generateWidgets(params), - navigation: await this.generateNavigation(params), - filters: await this.generateFilters(params) - }; - - return config; - } - - async validate(config: DashboardConfig): Promise { - try { - DashboardSchema.parse(config); - return true; - } catch (error) { - this.logger.warn('Dashboard validation failed', { error }); - return false; - } - } - - async getExamples(): Promise { - return [ - await this.generate({ - templateType: 'dashboard', - title: 'Sales Performance Dashboard', - description: 'Track sales metrics and team performance', - useCase: 'sales tracking for e-commerce business', - theme: 'light' - }), - await this.generate({ - templateType: 'dashboard', - title: 'DevOps Monitoring', - description: 'Monitor application health and infrastructure', - useCase: 'application monitoring and alerts', - theme: 'dark' - }), - await this.generate({ - templateType: 'dashboard', - title: 'Marketing Analytics', - description: 'Track campaign performance and ROI', - useCase: 'marketing campaign analysis', - theme: 'system' - }) - ]; - } - - private determineLayout(params: TemplateGenerationParams): 'grid' | 'columns' | 'rows' { - if (params.customData?.layout) { - return params.customData.layout as 'grid' | 'columns' | 'rows'; - } - - // Determine layout based on use case - const useCase = params.useCase?.toLowerCase() || ''; - - if (useCase.includes('monitoring') || useCase.includes('devops')) { - return 'rows'; // Better for monitoring data - } - - if (useCase.includes('financial') || useCase.includes('accounting')) { - return 'columns'; // Better for financial data - } - - return 'grid'; // Default flexible layout - } - - private async generateMetrics(params: TemplateGenerationParams): Promise { - // Use custom metrics if provided - if (params.customData?.metrics && Array.isArray(params.customData.metrics)) { - return params.customData.metrics; - } - - const useCase = params.useCase?.toLowerCase() || ''; - - // Generate metrics based on use case - if (useCase.includes('sales') || useCase.includes('revenue')) { - return this.generateSalesMetrics(); - } - - if (useCase.includes('marketing') || useCase.includes('campaign')) { - return this.generateMarketingMetrics(); - } - - if (useCase.includes('devops') || useCase.includes('monitoring')) { - return this.generateDevOpsMetrics(); - } - - if (useCase.includes('financial') || useCase.includes('finance')) { - return this.generateFinancialMetrics(); - } - - if (useCase.includes('ecommerce') || useCase.includes('e-commerce')) { - return this.generateEcommerceMetrics(); - } - - // Default business metrics - return this.generateDefaultMetrics(); - } - - private generateSalesMetrics(): DashboardConfig['metrics'] { - return [ - { - id: 'total-revenue', - label: 'Total Revenue', - value: '$124,530', - change: 12.5, - changeType: 'increase', - icon: 'DollarSign', - color: 'green', - unit: '$', - target: 150000, - trendData: [95000, 105000, 110000, 118000, 124530] - }, - { - id: 'new-customers', - label: 'New Customers', - value: 1247, - change: 8.2, - changeType: 'increase', - icon: 'Users', - color: 'blue', - target: 1500, - trendData: [1100, 1150, 1200, 1220, 1247] - }, - { - id: 'conversion-rate', - label: 'Conversion Rate', - value: '3.24%', - change: -0.3, - changeType: 'decrease', - icon: 'TrendingUp', - color: 'orange', - unit: '%', - target: 3.5, - trendData: [3.1, 3.3, 3.4, 3.3, 3.24] - }, - { - id: 'avg-order-value', - label: 'Avg Order Value', - value: '$89.50', - change: 5.7, - changeType: 'increase', - icon: 'ShoppingCart', - color: 'purple', - unit: '$', - target: 95, - trendData: [82, 84, 87, 88, 89.5] - } - ]; - } - - private generateMarketingMetrics(): DashboardConfig['metrics'] { - return [ - { - id: 'website-traffic', - label: 'Website Traffic', - value: 45230, - change: 15.3, - changeType: 'increase', - icon: 'Globe', - color: 'blue', - target: 50000, - trendData: [38000, 40000, 42000, 43500, 45230] - }, - { - id: 'lead-generation', - label: 'Leads Generated', - value: 892, - change: 22.1, - changeType: 'increase', - icon: 'Target', - color: 'green', - target: 1000, - trendData: [650, 720, 780, 850, 892] - }, - { - id: 'cost-per-click', - label: 'Cost Per Click', - value: '$2.45', - change: -8.5, - changeType: 'increase', - icon: 'MousePointer', - color: 'green', - unit: '$', - target: 2.0, - trendData: [2.8, 2.7, 2.6, 2.5, 2.45] - }, - { - id: 'email-open-rate', - label: 'Email Open Rate', - value: '24.8%', - change: 3.2, - changeType: 'increase', - icon: 'Mail', - color: 'purple', - unit: '%', - target: 25, - trendData: [22.1, 23.5, 24.0, 24.3, 24.8] - } - ]; - } - - private generateDevOpsMetrics(): DashboardConfig['metrics'] { - return [ - { - id: 'system-uptime', - label: 'System Uptime', - value: '99.94%', - change: 0.02, - changeType: 'increase', - icon: 'Activity', - color: 'green', - unit: '%', - target: 99.9, - trendData: [99.89, 99.91, 99.93, 99.92, 99.94] - }, - { - id: 'response-time', - label: 'Avg Response Time', - value: '245ms', - change: -12.3, - changeType: 'increase', - icon: 'Zap', - color: 'green', - unit: 'ms', - target: 200, - trendData: [280, 270, 260, 250, 245] - }, - { - id: 'error-rate', - label: 'Error Rate', - value: '0.12%', - change: -45.5, - changeType: 'increase', - icon: 'AlertTriangle', - color: 'green', - unit: '%', - target: 0.1, - trendData: [0.22, 0.18, 0.15, 0.13, 0.12] - }, - { - id: 'cpu-usage', - label: 'CPU Usage', - value: '67%', - change: 5.2, - changeType: 'decrease', - icon: 'Cpu', - color: 'orange', - unit: '%', - target: 70, - trendData: [62, 64, 65, 66, 67] - } - ]; - } - - private generateFinancialMetrics(): DashboardConfig['metrics'] { - return [ - { - id: 'total-assets', - label: 'Total Assets', - value: '$2.4M', - change: 7.8, - changeType: 'increase', - icon: 'Briefcase', - color: 'blue', - unit: '$', - trendData: [2.1, 2.2, 2.25, 2.35, 2.4] - }, - { - id: 'monthly-profit', - label: 'Monthly Profit', - value: '$45,230', - change: 12.4, - changeType: 'increase', - icon: 'TrendingUp', - color: 'green', - unit: '$', - target: 50000, - trendData: [38000, 40000, 42000, 43500, 45230] - }, - { - id: 'expense-ratio', - label: 'Expense Ratio', - value: '23.5%', - change: -2.1, - changeType: 'increase', - icon: 'PieChart', - color: 'green', - unit: '%', - target: 20, - trendData: [26.2, 25.8, 24.9, 24.1, 23.5] - }, - { - id: 'cash-flow', - label: 'Cash Flow', - value: '$12,850', - change: 18.7, - changeType: 'increase', - icon: 'DollarSign', - color: 'purple', - unit: '$', - trendData: [9800, 10200, 11000, 11800, 12850] - } - ]; - } - - private generateEcommerceMetrics(): DashboardConfig['metrics'] { - return [ - { - id: 'total-orders', - label: 'Total Orders', - value: 1852, - change: 16.3, - changeType: 'increase', - icon: 'ShoppingBag', - color: 'blue', - target: 2000, - trendData: [1450, 1580, 1680, 1750, 1852] - }, - { - id: 'cart-abandonment', - label: 'Cart Abandonment', - value: '68.2%', - change: -4.7, - changeType: 'increase', - icon: 'ShoppingCart', - color: 'green', - unit: '%', - target: 65, - trendData: [72.1, 71.3, 70.5, 69.1, 68.2] - }, - { - id: 'product-views', - label: 'Product Views', - value: 24680, - change: 28.9, - changeType: 'increase', - icon: 'Eye', - color: 'purple', - target: 30000, - trendData: [18000, 19500, 21000, 22800, 24680] - }, - { - id: 'return-rate', - label: 'Return Rate', - value: '2.8%', - change: -12.5, - changeType: 'increase', - icon: 'RotateCcw', - color: 'green', - unit: '%', - target: 2.5, - trendData: [3.5, 3.2, 3.0, 2.9, 2.8] - } - ]; - } - - private generateDefaultMetrics(): DashboardConfig['metrics'] { - return [ - { - id: 'total-users', - label: 'Total Users', - value: 12480, - change: 8.5, - changeType: 'increase', - icon: 'Users', - color: 'blue', - target: 15000, - trendData: [10200, 10800, 11400, 11900, 12480] - }, - { - id: 'active-sessions', - label: 'Active Sessions', - value: 847, - change: 12.3, - changeType: 'increase', - icon: 'Activity', - color: 'green', - target: 1000, - trendData: [680, 720, 760, 800, 847] - }, - { - id: 'completion-rate', - label: 'Completion Rate', - value: '76.4%', - change: 3.8, - changeType: 'increase', - icon: 'CheckCircle', - color: 'purple', - unit: '%', - target: 80, - trendData: [71.2, 73.5, 74.8, 75.6, 76.4] - }, - { - id: 'satisfaction-score', - label: 'Satisfaction Score', - value: '4.6/5', - change: 2.2, - changeType: 'increase', - icon: 'Star', - color: 'orange', - target: 4.8, - trendData: [4.3, 4.4, 4.5, 4.5, 4.6] - } - ]; - } - - private async generateCharts(params: TemplateGenerationParams): Promise { - if (params.customData?.charts && Array.isArray(params.customData.charts)) { - return params.customData.charts; - } - - const useCase = params.useCase?.toLowerCase() || ''; - - // Generate charts based on use case - const charts: DashboardConfig['charts'] = [ - { - id: 'trend-chart', - type: 'line', - title: 'Performance Trend', - description: 'Track performance over time', - data: { - labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun'], - datasets: [{ - label: 'Performance', - data: [65, 78, 82, 88, 92, 95], - borderColor: params.primaryColor || '#3b82f6', - backgroundColor: `${params.primaryColor || '#3b82f6'}20`, - fill: true - }] - } - } - ]; - - if (useCase.includes('sales') || useCase.includes('revenue')) { - charts.push({ - id: 'revenue-breakdown', - type: 'pie', - title: 'Revenue by Category', - data: { - labels: ['Product Sales', 'Services', 'Subscriptions', 'Other'], - datasets: [{ - label: 'Revenue', - data: [45, 25, 20, 10], - backgroundColor: ['#3b82f6', '#10b981', '#f59e0b', '#ef4444'] - }] - } - }); - } - - return charts; - } - - private async generateActivity(params: TemplateGenerationParams): Promise { - if (params.customData?.recentActivity && Array.isArray(params.customData.recentActivity)) { - return params.customData.recentActivity; - } - - return [ - { - id: 'activity-1', - type: 'user_action', - title: 'New user registered', - description: 'john.doe@example.com completed registration', - timestamp: new Date(Date.now() - 5 * 60 * 1000).toISOString(), - user: { - name: 'John Doe', - avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=32&h=32&fit=crop&crop=face' - }, - status: 'success', - icon: 'UserPlus' - }, - { - id: 'activity-2', - type: 'system_event', - title: 'System backup completed', - description: 'Daily backup process finished successfully', - timestamp: new Date(Date.now() - 15 * 60 * 1000).toISOString(), - status: 'info', - icon: 'Database' - }, - { - id: 'activity-3', - type: 'alert', - title: 'High CPU usage detected', - description: 'Server load exceeded 80% threshold', - timestamp: new Date(Date.now() - 30 * 60 * 1000).toISOString(), - status: 'warning', - icon: 'AlertTriangle' - } - ]; - } - - private async generateWidgets(params: TemplateGenerationParams): Promise { - if (params.customData?.widgets && Array.isArray(params.customData.widgets)) { - return params.customData.widgets; - } - - return [ - { - id: 'quick-stats', - type: 'stats', - title: 'Quick Stats', - size: 'small', - position: { x: 0, y: 0, width: 4, height: 2 } - }, - { - id: 'performance-chart', - type: 'chart', - title: 'Performance Overview', - size: 'large', - position: { x: 4, y: 0, width: 8, height: 4 } - } - ]; - } - - private async generateNavigation(params: TemplateGenerationParams): Promise { - if (params.customData?.navigation) { - return params.customData.navigation as any; - } - - const useCase = params.useCase?.toLowerCase() || ''; - - let items = [ - { id: 'overview', label: 'Overview', icon: 'Home', active: true }, - { id: 'analytics', label: 'Analytics', icon: 'BarChart', active: false }, - { id: 'reports', label: 'Reports', icon: 'FileText', active: false }, - { id: 'settings', label: 'Settings', icon: 'Settings', active: false } - ]; - - if (useCase.includes('sales')) { - items = [ - { id: 'dashboard', label: 'Dashboard', icon: 'Home', active: true }, - { id: 'sales', label: 'Sales', icon: 'TrendingUp', active: false }, - { id: 'customers', label: 'Customers', icon: 'Users', active: false }, - { id: 'products', label: 'Products', icon: 'Package', active: false }, - { id: 'reports', label: 'Reports', icon: 'FileText', active: false } - ]; - } - - return { - items, - showSearch: true, - showNotifications: true, - showProfile: true - }; - } - - private async generateFilters(params: TemplateGenerationParams): Promise { - if (params.customData?.filters) { - return params.customData.filters; - } - - return { - timeRange: { - options: ['Last 7 days', 'Last 30 days', 'Last 3 months', 'Last year', 'Custom range'], - default: 'Last 30 days' - }, - categories: ['All', 'Sales', 'Marketing', 'Operations', 'Finance'], - customFilters: [ - { - id: 'region', - label: 'Region', - type: 'select', - options: ['All Regions', 'North America', 'Europe', 'Asia Pacific', 'Latin America'] - }, - { - id: 'team', - label: 'Team', - type: 'multi-select', - options: ['Sales Team', 'Marketing Team', 'Product Team', 'Support Team'] - } - ] - }; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/data-table.ts b/mcp-ui-server-v2/src/templates/generators/data-table.ts deleted file mode 100644 index d9dcbf1..0000000 --- a/mcp-ui-server-v2/src/templates/generators/data-table.ts +++ /dev/null @@ -1,1406 +0,0 @@ -/** - * Data Table Generator - Dynamic table templates with advanced features - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -// Column configuration schema -const ColumnSchema = z.object({ - id: z.string(), - accessorKey: z.string(), - header: z.string(), - type: z.enum(['text', 'number', 'date', 'boolean', 'enum', 'image', 'link', 'badge', 'progress', 'avatar', 'actions']).default('text'), - sortable: z.boolean().default(true), - filterable: z.boolean().default(true), - width: z.union([z.number(), z.string()]).optional(), - minWidth: z.number().optional(), - maxWidth: z.number().optional(), - align: z.enum(['left', 'center', 'right']).default('left'), - fixed: z.enum(['left', 'right']).optional(), - hidden: z.boolean().default(false), - format: z.object({ - type: z.enum(['currency', 'percentage', 'date', 'datetime', 'number']).optional(), - options: z.record(z.any()).optional() - }).optional(), - enum: z.array(z.object({ - value: z.any(), - label: z.string(), - color: z.string().optional(), - icon: z.string().optional() - })).optional(), - render: z.object({ - component: z.string().optional(), - props: z.record(z.any()).optional() - }).optional() -}); - -// Filter configuration schema -const FilterSchema = z.object({ - id: z.string(), - column: z.string(), - type: z.enum(['text', 'number', 'date', 'select', 'multiselect', 'range', 'boolean']), - label: z.string(), - placeholder: z.string().optional(), - options: z.array(z.object({ - value: z.any(), - label: z.string() - })).optional(), - defaultValue: z.any().optional() -}); - -// Action configuration schema -const ActionSchema = z.object({ - id: z.string(), - label: z.string(), - icon: z.string().optional(), - variant: z.enum(['primary', 'secondary', 'outline', 'ghost', 'destructive']).default('outline'), - size: z.enum(['sm', 'md', 'lg']).default('sm'), - href: z.string().optional(), - onClick: z.string().optional(), - disabled: z.boolean().default(false), - showInDropdown: z.boolean().default(false), - confirm: z.object({ - title: z.string(), - description: z.string(), - confirmText: z.string().default('Confirm'), - cancelText: z.string().default('Cancel') - }).optional() -}); - -// Data table configuration schema -const DataTableConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - columns: z.array(ColumnSchema), - data: z.array(z.record(z.any())).default([]), - features: z.object({ - sorting: z.object({ - enabled: z.boolean().default(true), - multiSort: z.boolean().default(false), - defaultSort: z.array(z.object({ - column: z.string(), - direction: z.enum(['asc', 'desc']) - })).optional() - }), - filtering: z.object({ - enabled: z.boolean().default(true), - global: z.boolean().default(true), - globalPlaceholder: z.string().default('Search...'), - columnFilters: z.array(FilterSchema).default([]) - }), - pagination: z.object({ - enabled: z.boolean().default(true), - pageSize: z.number().default(10), - pageSizeOptions: z.array(z.number()).default([5, 10, 20, 50, 100]), - showTotal: z.boolean().default(true), - showPageInfo: z.boolean().default(true) - }), - selection: z.object({ - enabled: z.boolean().default(false), - type: z.enum(['single', 'multiple']).default('multiple'), - selectAll: z.boolean().default(true), - onSelectionChange: z.string().optional() - }), - export: z.object({ - enabled: z.boolean().default(false), - formats: z.array(z.enum(['csv', 'excel', 'pdf', 'json'])).default(['csv']), - filename: z.string().optional() - }), - refresh: z.object({ - enabled: z.boolean().default(false), - interval: z.number().optional(), - onRefresh: z.string().optional() - }) - }), - actions: z.object({ - row: z.array(ActionSchema).default([]), - bulk: z.array(ActionSchema).default([]), - toolbar: z.array(ActionSchema).default([]) - }), - styling: z.object({ - theme: z.enum(['light', 'dark', 'auto']).default('auto'), - size: z.enum(['sm', 'md', 'lg']).default('md'), - bordered: z.boolean().default(true), - striped: z.boolean().default(false), - hover: z.boolean().default(true), - compact: z.boolean().default(false), - stickyHeader: z.boolean().default(false), - maxHeight: z.string().optional(), - className: z.string().optional() - }), - responsive: z.object({ - enabled: z.boolean().default(true), - breakpoint: z.enum(['sm', 'md', 'lg', 'xl']).default('md'), - stackedLayout: z.boolean().default(true), - hiddenColumns: z.record(z.array(z.string())).optional() - }), - loading: z.object({ - enabled: z.boolean().default(false), - skeleton: z.boolean().default(true), - rows: z.number().default(5) - }), - empty: z.object({ - title: z.string().default('No data available'), - description: z.string().default('There are no items to display'), - icon: z.string().optional(), - action: ActionSchema.optional() - }) -}); - -type DataTableConfig = z.infer; - -export class DataTableGenerator implements TemplateGenerator { - name = 'Dynamic Data Table Generator'; - description = 'Generate feature-rich data tables with sorting, filtering, pagination, and responsive design'; - capabilities = [ - 'Advanced sorting', - 'Column filtering', - 'Global search', - 'Pagination', - 'Row selection', - 'Export functionality', - 'Responsive design', - 'Custom actions', - 'Real-time updates', - 'Custom cell rendering' - ]; - useCases = [ - 'User management tables', - 'Product listings', - 'Order management', - 'Analytics dashboards', - 'Inventory systems', - 'Customer databases', - 'Financial data', - 'Project management', - 'Content management', - 'Admin panels' - ]; - schema = DataTableConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - // Use custom table data if provided - if (params.customData?.table) { - return this.validateAndEnhanceCustomTable(params.customData.table); - } - - // Generate table based on use case - return this.generateTableByUseCase(params); - } - - async validate(config: DataTableConfig): Promise { - try { - DataTableConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [ - await this.generateUserTable(), - await this.generateProductTable(), - await this.generateOrderTable(), - await this.generateAnalyticsTable() - ]; - } - - private validateAndEnhanceCustomTable(customTable: any): DataTableConfig { - const validated = DataTableConfigSchema.parse(customTable); - return this.enhanceTableWithDefaults(validated); - } - - private generateTableByUseCase(params: TemplateGenerationParams): DataTableConfig { - const useCase = params.useCase?.toLowerCase() || ''; - const title = params.title || 'Data Table'; - - if (useCase.includes('user') || useCase.includes('member') || useCase.includes('customer')) { - return this.generateUserTable(title); - } else if (useCase.includes('product') || useCase.includes('inventory') || useCase.includes('catalog')) { - return this.generateProductTable(title); - } else if (useCase.includes('order') || useCase.includes('transaction') || useCase.includes('purchase')) { - return this.generateOrderTable(title); - } else if (useCase.includes('analytics') || useCase.includes('metrics') || useCase.includes('performance')) { - return this.generateAnalyticsTable(title); - } else if (useCase.includes('project') || useCase.includes('task')) { - return this.generateProjectTable(title); - } else if (useCase.includes('financial') || useCase.includes('invoice') || useCase.includes('payment')) { - return this.generateFinancialTable(title); - } else if (useCase.includes('content') || useCase.includes('article') || useCase.includes('post')) { - return this.generateContentTable(title); - } else { - return this.generateGenericTable(title, params); - } - } - - private generateUserTable(title: string = 'Users'): DataTableConfig { - return { - id: 'users-table', - title, - description: 'Manage user accounts and permissions', - columns: [ - { - id: 'avatar', - accessorKey: 'avatar', - header: '', - type: 'avatar', - width: 60, - sortable: false, - filterable: false, - align: "left", - hidden: false - }, - { - id: 'name', - accessorKey: 'name', - header: 'Name', - type: 'text', - sortable: true, - filterable: true, - minWidth: 150, - align: "left", - hidden: false - }, - { - id: 'email', - accessorKey: 'email', - header: 'Email', - type: 'text', - sortable: true, - filterable: true, - minWidth: 200, - align: "left", - hidden: false - }, - { - id: 'role', - accessorKey: 'role', - header: 'Role', - type: 'badge', - sortable: true, - filterable: true, - enum: [ - { value: 'admin', label: 'Admin', color: 'red' }, - { value: 'editor', label: 'Editor', color: 'blue' }, - { value: 'user', label: 'User', color: 'green' }, - { value: 'guest', label: 'Guest', color: 'gray' } - ], - align: "left", - hidden: false - }, - { - id: 'status', - accessorKey: 'status', - header: 'Status', - type: 'badge', - sortable: true, - filterable: true, - enum: [ - { value: 'active', label: 'Active', color: 'green' }, - { value: 'inactive', label: 'Inactive', color: 'gray' }, - { value: 'suspended', label: 'Suspended', color: 'red' } - ], - align: "left", - hidden: false - }, - { - id: 'lastLogin', - accessorKey: 'lastLogin', - header: 'Last Login', - type: 'date', - sortable: true, - filterable: true, - format: { - type: 'datetime', - options: { relative: true } - }, - align: "left", - hidden: false - }, - { - id: 'createdAt', - accessorKey: 'createdAt', - header: 'Created', - type: 'date', - sortable: true, - filterable: true, - format: { - type: 'date' - }, - align: "left", - hidden: false - }, - { - id: 'actions', - accessorKey: 'actions', - header: 'Actions', - type: 'actions', - width: 120, - sortable: false, - filterable: false, - fixed: 'right', - align: "center", - hidden: false - } - ], - data: this.generateSampleUserData(), - features: { - sorting: { - enabled: true, - multiSort: true, - defaultSort: [{ column: 'name', direction: 'asc' }] - }, - filtering: { - enabled: true, - global: true, - globalPlaceholder: 'Search users...', - columnFilters: [ - { - id: 'role-filter', - column: 'role', - type: 'select', - label: 'Role', - options: [ - { value: 'admin', label: 'Admin' }, - { value: 'editor', label: 'Editor' }, - { value: 'user', label: 'User' }, - { value: 'guest', label: 'Guest' } - ] - }, - { - id: 'status-filter', - column: 'status', - type: 'select', - label: 'Status', - options: [ - { value: 'active', label: 'Active' }, - { value: 'inactive', label: 'Inactive' }, - { value: 'suspended', label: 'Suspended' } - ] - } - ] - }, - pagination: { - enabled: true, - pageSize: 20, - pageSizeOptions: [10, 20, 50, 100], - showTotal: true, - showPageInfo: true - }, - selection: { - enabled: true, - type: 'multiple', - selectAll: true - }, - export: { - enabled: true, - formats: ['csv', 'excel'], - filename: 'users-export' - }, - refresh: { - enabled: true, - onRefresh: 'refreshUsers' - } - }, - actions: { - row: [ - { - id: 'view', - label: 'View', - icon: 'Eye', - variant: 'ghost', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'viewUser' - }, - { - id: 'edit', - label: 'Edit', - icon: 'Edit', - variant: 'ghost', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'editUser' - }, - { - id: 'delete', - label: 'Delete', - icon: 'Trash', - variant: 'destructive', - size: "md", - disabled: false, - onClick: 'deleteUser', - showInDropdown: true, - confirm: { - title: 'Delete User', - description: 'Are you sure you want to delete this user? This action cannot be undone.', - confirmText: "Confirm", - cancelText: "Cancel" - } - } - ], - bulk: [ - { - id: 'bulk-delete', - label: 'Delete Selected', - icon: 'Trash', - variant: 'destructive', - onClick: 'bulkDeleteUsers', - size: "md", - disabled: false, - showInDropdown: false, - confirm: { - title: 'Delete Users', - description: 'Are you sure you want to delete the selected users?', - confirmText: "Confirm", - cancelText: "Cancel" - } - }, - { - id: 'bulk-export', - label: 'Export Selected', - icon: 'Download', - variant: 'outline', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'bulkExportUsers' - } - ], - toolbar: [ - { - id: 'add-user', - label: 'Add User', - icon: 'Plus', - variant: 'primary', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'addUser' - } - ] - }, - styling: { - theme: 'auto', - size: 'md', - bordered: true, - striped: false, - hover: true, - compact: false, - stickyHeader: true - }, - responsive: { - enabled: true, - breakpoint: 'md', - stackedLayout: true, - hiddenColumns: { - sm: ['createdAt', 'lastLogin'], - xs: ['createdAt', 'lastLogin', 'role'] - } - }, - loading: { - enabled: false, - skeleton: true, - rows: 10 - }, - empty: { - title: 'No users found', - description: 'Get started by adding your first user', - icon: 'Users', - action: { - id: 'add-first-user', - label: 'Add User', - icon: 'Plus', - variant: 'primary', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'addUser' - } - } - }; - } - - private generateProductTable(title: string = 'Products'): DataTableConfig { - return { - id: 'products-table', - title, - description: 'Manage your product inventory', - columns: [ - { - id: 'image', - accessorKey: 'image', - header: 'Image', - type: 'image', - width: 80, - sortable: false, - filterable: false, - align: "left", - hidden: false - }, - { - id: 'name', - accessorKey: 'name', - header: 'Product Name', - type: 'text', - sortable: true, - filterable: true, - minWidth: 200, - align: "left", - hidden: false - }, - { - id: 'sku', - accessorKey: 'sku', - header: 'SKU', - type: 'text', - sortable: true, - filterable: true, - width: 120, - align: "left", - hidden: false - }, - { - id: 'category', - accessorKey: 'category', - header: 'Category', - type: 'badge', - sortable: true, - filterable: true, - enum: [ - { value: 'electronics', label: 'Electronics', color: 'blue' }, - { value: 'clothing', label: 'Clothing', color: 'green' }, - { value: 'books', label: 'Books', color: 'purple' }, - { value: 'home', label: 'Home & Garden', color: 'orange' } - ], - align: "left", - hidden: false - }, - { - id: 'price', - accessorKey: 'price', - header: 'Price', - type: 'number', - sortable: true, - filterable: true, - align: 'right', - format: { - type: 'currency', - options: { currency: 'USD' } - }, - hidden: false - }, - { - id: 'stock', - accessorKey: 'stock', - header: 'Stock', - type: 'number', - sortable: true, - filterable: true, - align: 'center', - hidden: false - }, - { - id: 'status', - accessorKey: 'status', - header: 'Status', - type: 'badge', - sortable: true, - filterable: true, - enum: [ - { value: 'active', label: 'Active', color: 'green' }, - { value: 'draft', label: 'Draft', color: 'gray' }, - { value: 'out-of-stock', label: 'Out of Stock', color: 'red' } - ], - align: "left", - hidden: false - }, - { - id: 'actions', - accessorKey: 'actions', - header: 'Actions', - type: 'actions', - width: 120, - sortable: false, - filterable: false, - fixed: 'right', - align: "center", - hidden: false - } - ], - data: this.generateSampleProductData(), - features: { - sorting: { - enabled: true, - multiSort: false, - defaultSort: [{ column: 'name', direction: 'asc' }] - }, - filtering: { - enabled: true, - global: true, - globalPlaceholder: 'Search products...', - columnFilters: [ - { - id: 'category-filter', - column: 'category', - type: 'select', - label: 'Category', - options: [ - { value: 'electronics', label: 'Electronics' }, - { value: 'clothing', label: 'Clothing' }, - { value: 'books', label: 'Books' }, - { value: 'home', label: 'Home & Garden' } - ] - }, - { - id: 'price-filter', - column: 'price', - type: 'range', - label: 'Price Range' - } - ] - }, - pagination: { - enabled: true, - pageSize: 15, - pageSizeOptions: [10, 15, 25, 50], - showTotal: true, - showPageInfo: true - }, - selection: { - enabled: true, - type: 'multiple', - selectAll: true - }, - export: { - enabled: true, - formats: ['csv', 'excel'], - filename: 'products-export' - }, - refresh: { - enabled: true, - onRefresh: 'refreshProducts' - } - }, - actions: { - row: [ - { - id: 'edit', - label: 'Edit', - icon: 'Edit', - variant: 'ghost', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'editProduct' - }, - { - id: 'duplicate', - label: 'Duplicate', - icon: 'Copy', - variant: 'ghost', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'duplicateProduct' - }, - { - id: 'delete', - label: 'Delete', - icon: 'Trash', - variant: 'destructive', - size: "md", - disabled: false, - showInDropdown: true, - onClick: 'deleteProduct', - confirm: { - title: 'Delete Product', - description: 'Are you sure you want to delete this product?', - confirmText: "Confirm", - cancelText: "Cancel" - } - } - ], - bulk: [ - { - id: 'bulk-update-status', - label: 'Update Status', - icon: 'RefreshCw', - variant: 'outline', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'bulkUpdateStatus' - } - ], - toolbar: [ - { - id: 'add-product', - label: 'Add Product', - icon: 'Plus', - variant: 'primary', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'addProduct' - }, - { - id: 'import', - label: 'Import', - icon: 'Upload', - variant: 'outline', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'importProducts' - } - ] - }, - styling: { - theme: 'auto', - size: 'md', - bordered: true, - striped: true, - hover: true, - compact: false, - stickyHeader: true - }, - responsive: { - enabled: true, - breakpoint: 'lg', - stackedLayout: true - }, - loading: { - enabled: false, - skeleton: true, - rows: 8 - }, - empty: { - title: 'No products found', - description: 'Start building your product catalog', - icon: 'Package', - action: { - id: 'add-first-product', - label: 'Add Product', - icon: 'Plus', - variant: 'primary', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'addProduct' - } - } - }; - } - - private generateOrderTable(title: string = 'Orders'): DataTableConfig { - return { - id: 'orders-table', - title, - description: 'Track and manage customer orders', - columns: [ - { - id: 'orderNumber', - accessorKey: 'orderNumber', - header: 'Order #', - type: 'link', - sortable: true, - filterable: true, - width: 120, - align: "left", - hidden: false - }, - { - id: 'customer', - accessorKey: 'customer', - header: 'Customer', - type: 'text', - sortable: true, - filterable: true, - minWidth: 150, - align: "left", - hidden: false - }, - { - id: 'date', - accessorKey: 'date', - header: 'Order Date', - type: 'date', - sortable: true, - filterable: true, - format: { - type: 'date' - }, - align: "left", - hidden: false - }, - { - id: 'status', - accessorKey: 'status', - header: 'Status', - type: 'badge', - sortable: true, - filterable: true, - enum: [ - { value: 'pending', label: 'Pending', color: 'yellow' }, - { value: 'processing', label: 'Processing', color: 'blue' }, - { value: 'shipped', label: 'Shipped', color: 'purple' }, - { value: 'delivered', label: 'Delivered', color: 'green' }, - { value: 'cancelled', label: 'Cancelled', color: 'red' } - ], - align: "left", - hidden: false - }, - { - id: 'total', - accessorKey: 'total', - header: 'Total', - type: 'number', - sortable: true, - filterable: true, - align: 'right', - format: { - type: 'currency', - options: { currency: 'USD' } - }, - hidden: false - }, - { - id: 'items', - accessorKey: 'items', - header: 'Items', - type: 'number', - sortable: true, - filterable: false, - align: 'center', - hidden: false - }, - { - id: 'actions', - accessorKey: 'actions', - header: 'Actions', - type: 'actions', - width: 120, - sortable: false, - filterable: false, - fixed: 'right', - align: "center", - hidden: false - } - ], - data: this.generateSampleOrderData(), - features: { - sorting: { - enabled: true, - multiSort: false, - defaultSort: [{ column: 'date', direction: 'desc' }] - }, - filtering: { - enabled: true, - global: true, - globalPlaceholder: 'Search orders...', - columnFilters: [ - { - id: 'status-filter', - column: 'status', - type: 'multiselect', - label: 'Status', - options: [ - { value: 'pending', label: 'Pending' }, - { value: 'processing', label: 'Processing' }, - { value: 'shipped', label: 'Shipped' }, - { value: 'delivered', label: 'Delivered' }, - { value: 'cancelled', label: 'Cancelled' } - ] - }, - { - id: 'date-filter', - column: 'date', - type: 'date', - label: 'Order Date' - } - ] - }, - pagination: { - enabled: true, - pageSize: 25, - pageSizeOptions: [10, 25, 50, 100], - showTotal: true, - showPageInfo: true - }, - selection: { - enabled: true, - type: 'multiple', - selectAll: true - }, - export: { - enabled: true, - formats: ['csv', 'excel', 'pdf'], - filename: 'orders-export' - }, - refresh: { - enabled: true, - interval: 30000, - onRefresh: 'refreshOrders' - } - }, - actions: { - row: [ - { - id: 'view', - label: 'View Details', - icon: 'Eye', - variant: 'ghost', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'viewOrder' - }, - { - id: 'print', - label: 'Print', - icon: 'Printer', - variant: 'ghost', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'printOrder' - }, - { - id: 'cancel', - label: 'Cancel Order', - icon: 'X', - variant: 'destructive', - size: "md", - disabled: false, - showInDropdown: true, - onClick: 'cancelOrder', - confirm: { - title: 'Cancel Order', - description: 'Are you sure you want to cancel this order?', - confirmText: "Confirm", - cancelText: "Cancel" - } - } - ], - bulk: [ - { - id: 'bulk-update-status', - label: 'Update Status', - icon: 'RefreshCw', - variant: 'outline', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'bulkUpdateOrderStatus' - }, - { - id: 'bulk-export', - label: 'Export Selected', - icon: 'Download', - variant: 'outline', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'bulkExportOrders' - } - ], - toolbar: [ - { - id: 'create-order', - label: 'Create Order', - icon: 'Plus', - variant: 'primary', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'createOrder' - } - ] - }, - styling: { - theme: 'auto', - size: 'md', - bordered: true, - striped: false, - hover: true, - compact: false, - stickyHeader: true - }, - responsive: { - enabled: true, - breakpoint: 'lg', - stackedLayout: true, - hiddenColumns: { - md: ['items'], - sm: ['items', 'date'] - } - }, - loading: { - enabled: false, - skeleton: true, - rows: 12 - }, - empty: { - title: 'No orders found', - description: 'Orders will appear here when customers make purchases', - icon: 'ShoppingCart' - } - }; - } - - private generateAnalyticsTable(title: string = 'Analytics Data'): DataTableConfig { - return { - id: 'analytics-table', - title, - description: 'View performance metrics and analytics', - columns: [ - { - id: 'metric', - accessorKey: 'metric', - header: 'Metric', - type: 'text', - sortable: true, - filterable: true, - minWidth: 180, - align: "left", - hidden: false - }, - { - id: 'value', - accessorKey: 'value', - header: 'Current Value', - type: 'number', - sortable: true, - filterable: true, - align: 'right', - format: { - type: 'number', - options: { notation: 'compact' } - }, - hidden: false - }, - { - id: 'change', - accessorKey: 'change', - header: 'Change', - type: 'number', - sortable: true, - filterable: true, - align: 'right', - format: { - type: 'percentage' - }, - hidden: false - }, - { - id: 'trend', - accessorKey: 'trend', - header: 'Trend', - type: 'progress', - sortable: false, - filterable: false, - width: 100, - align: "center", - hidden: false - }, - { - id: 'category', - accessorKey: 'category', - header: 'Category', - type: 'badge', - sortable: true, - filterable: true, - enum: [ - { value: 'traffic', label: 'Traffic', color: 'blue' }, - { value: 'conversion', label: 'Conversion', color: 'green' }, - { value: 'revenue', label: 'Revenue', color: 'purple' }, - { value: 'engagement', label: 'Engagement', color: 'orange' } - ], - align: "left", - hidden: false - }, - { - id: 'lastUpdated', - accessorKey: 'lastUpdated', - header: 'Last Updated', - type: 'date', - sortable: true, - filterable: true, - format: { - type: 'datetime', - options: { relative: true } - }, - align: "left", - hidden: false - } - ], - data: this.generateSampleAnalyticsData(), - features: { - sorting: { - enabled: true, - multiSort: true, - defaultSort: [{ column: 'category', direction: 'asc' }] - }, - filtering: { - enabled: true, - global: true, - globalPlaceholder: 'Search metrics...', - columnFilters: [ - { - id: 'category-filter', - column: 'category', - type: 'select', - label: 'Category', - options: [ - { value: 'traffic', label: 'Traffic' }, - { value: 'conversion', label: 'Conversion' }, - { value: 'revenue', label: 'Revenue' }, - { value: 'engagement', label: 'Engagement' } - ] - } - ] - }, - pagination: { - enabled: true, - pageSize: 20, - pageSizeOptions: [10, 20, 50], - showTotal: true, - showPageInfo: true - }, - selection: { - enabled: false, - type: 'single', - selectAll: false - }, - export: { - enabled: true, - formats: ['csv', 'excel'], - filename: 'analytics-export' - }, - refresh: { - enabled: true, - interval: 60000, - onRefresh: 'refreshAnalytics' - } - }, - actions: { - row: [ - { - id: 'view-details', - label: 'View Details', - icon: 'BarChart', - variant: 'ghost', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'viewMetricDetails' - } - ], - bulk: [], - toolbar: [ - { - id: 'refresh-all', - label: 'Refresh All', - icon: 'RefreshCw', - variant: 'outline', - size: "md", - disabled: false, - showInDropdown: false, - onClick: 'refreshAllMetrics' - } - ] - }, - styling: { - theme: 'auto', - size: 'md', - bordered: true, - striped: true, - hover: true, - compact: false, - stickyHeader: true - }, - responsive: { - enabled: true, - breakpoint: 'md', - stackedLayout: true, - hiddenColumns: { - sm: ['lastUpdated'], - xs: ['lastUpdated', 'trend'] - } - }, - loading: { - enabled: false, - skeleton: true, - rows: 15 - }, - empty: { - title: 'No metrics available', - description: 'Analytics data will appear here once configured', - icon: 'BarChart' - } - }; - } - - private generateProjectTable(title: string = 'Projects'): DataTableConfig { - // Implementation similar to above patterns - return this.generateGenericTable(title, { templateType: 'dataTable', title, useCase: 'project management' }); - } - - private generateFinancialTable(title: string = 'Financial Data'): DataTableConfig { - // Implementation similar to above patterns - return this.generateGenericTable(title, { templateType: 'dataTable', title, useCase: 'financial tracking' }); - } - - private generateContentTable(title: string = 'Content'): DataTableConfig { - // Implementation similar to above patterns - return this.generateGenericTable(title, { templateType: 'dataTable', title, useCase: 'content management' }); - } - - private generateGenericTable(title: string, params: TemplateGenerationParams): DataTableConfig { - const columnCount = Number(params.customData?.columnCount) || 5; - const columns = []; - - for (let i = 0; i < columnCount; i++) { - columns.push({ - id: `column_${i}`, - accessorKey: `column_${i}`, - header: `Column ${i + 1}`, - type: 'text' as const, - sortable: true, - filterable: true, - align: 'left' as const, - hidden: false - }); - } - - return { - id: 'generic-table', - title, - description: 'A dynamically generated data table', - columns, - data: [], - features: { - sorting: { enabled: true, multiSort: false }, - filtering: { enabled: true, global: true, globalPlaceholder: 'Search...', columnFilters: [] }, - pagination: { enabled: true, pageSize: 10, pageSizeOptions: [5, 10, 20, 50], showTotal: true, showPageInfo: true }, - selection: { enabled: false, type: 'multiple', selectAll: true }, - export: { enabled: false, formats: ['csv'], filename: 'export' }, - refresh: { enabled: false } - }, - actions: { row: [], bulk: [], toolbar: [] }, - styling: { theme: 'auto', size: 'md', compact: false, bordered: true, striped: false, hover: true, stickyHeader: false }, - responsive: { enabled: true, breakpoint: 'md', stackedLayout: true }, - loading: { enabled: false, skeleton: true, rows: 5 }, - empty: { title: 'No data available', description: 'Data will appear here when available' } - }; - } - - private enhanceTableWithDefaults(table: DataTableConfig): DataTableConfig { - // Add any missing default values or enhancements - return table; - } - - // Sample data generators - private generateSampleUserData() { - return [ - { - id: '1', - avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150', - name: 'John Doe', - email: 'john@example.com', - role: 'admin', - status: 'active', - lastLogin: new Date('2024-01-15T10:30:00Z'), - createdAt: new Date('2023-06-01T00:00:00Z') - }, - { - id: '2', - avatar: 'https://images.unsplash.com/photo-1494790108755-2616b612b000?w=150', - name: 'Jane Smith', - email: 'jane@example.com', - role: 'editor', - status: 'active', - lastLogin: new Date('2024-01-14T15:20:00Z'), - createdAt: new Date('2023-07-15T00:00:00Z') - } - ]; - } - - private generateSampleProductData() { - return [ - { - id: '1', - image: 'https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?w=150', - name: 'Wireless Headphones', - sku: 'WH-001', - category: 'electronics', - price: 199.99, - stock: 45, - status: 'active' - }, - { - id: '2', - image: 'https://images.unsplash.com/photo-1441986300917-64674bd600d8?w=150', - name: 'Cotton T-Shirt', - sku: 'CT-002', - category: 'clothing', - price: 29.99, - stock: 0, - status: 'out-of-stock' - } - ]; - } - - private generateSampleOrderData() { - return [ - { - id: '1', - orderNumber: 'ORD-001', - customer: 'John Doe', - date: new Date('2024-01-15T00:00:00Z'), - status: 'delivered', - total: 299.98, - items: 2 - }, - { - id: '2', - orderNumber: 'ORD-002', - customer: 'Jane Smith', - date: new Date('2024-01-14T00:00:00Z'), - status: 'processing', - total: 149.99, - items: 1 - } - ]; - } - - private generateSampleAnalyticsData() { - return [ - { - id: '1', - metric: 'Page Views', - value: 125000, - change: 12.5, - trend: 85, - category: 'traffic', - lastUpdated: new Date('2024-01-15T12:00:00Z') - }, - { - id: '2', - metric: 'Conversion Rate', - value: 3.2, - change: -5.1, - trend: 65, - category: 'conversion', - lastUpdated: new Date('2024-01-15T11:30:00Z') - } - ]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/ecommerce.ts b/mcp-ui-server-v2/src/templates/generators/ecommerce.ts deleted file mode 100644 index eaeea18..0000000 --- a/mcp-ui-server-v2/src/templates/generators/ecommerce.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Ecommerce Generator - E-commerce and shopping - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const EcommerceConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional() -}); - -type EcommerceConfig = z.infer; - -export class EcommerceGenerator implements TemplateGenerator { - name = 'Ecommerce Generator'; - description = 'Generate e-commerce and shopping interfaces'; - capabilities = ['Product displays', 'Shopping cart', 'Checkout process']; - useCases = ['Online stores', 'Product pages', 'Shopping experiences']; - schema = EcommerceConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'ecommerce-store', - title: params.title || 'Online Store', - description: 'Complete e-commerce shopping experience' - }; - } - - async validate(config: EcommerceConfig): Promise { - try { - EcommerceConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'ecommerce', title: 'Example Store' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/feed.ts b/mcp-ui-server-v2/src/templates/generators/feed.ts deleted file mode 100644 index 8b99aaf..0000000 --- a/mcp-ui-server-v2/src/templates/generators/feed.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Feed Generator - Activity feeds and timelines - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const FeedConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - items: z.array(z.object({ - id: z.string(), - type: z.enum(['post', 'comment', 'like', 'share']).default('post'), - author: z.string(), - content: z.string(), - timestamp: z.string(), - avatar: z.string().optional() - })).default([]) -}); - -type FeedConfig = z.infer; - -export class FeedGenerator implements TemplateGenerator { - name = 'Feed Generator'; - description = 'Generate activity feeds and social media style content'; - capabilities = ['Activity streams', 'Social feeds', 'Real-time updates']; - useCases = ['Social media', 'Activity logs', 'News feeds', 'Notification centers']; - schema = FeedConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'activity-feed', - title: params.title || 'Activity Feed', - description: 'Recent activity and updates', - items: [ - { - id: '1', - type: 'post', - author: 'John Doe', - content: 'Just completed the new project milestone!', - timestamp: '2024-01-15T10:30:00Z', - avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=150' - } - ] - }; - } - - async validate(config: FeedConfig): Promise { - try { - FeedConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'feed', title: 'Example Feed' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/form.ts b/mcp-ui-server-v2/src/templates/generators/form.ts deleted file mode 100644 index bd316f3..0000000 --- a/mcp-ui-server-v2/src/templates/generators/form.ts +++ /dev/null @@ -1,1066 +0,0 @@ -/** - * Form Generator - Dynamic form template with comprehensive field types - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -// Field type definitions -const FormFieldSchema = z.object({ - id: z.string(), - type: z.enum(['text', 'email', 'password', 'number', 'tel', 'url', 'textarea', 'select', 'radio', 'checkbox', 'file', 'date', 'time', 'datetime-local', 'range', 'color']), - label: z.string(), - placeholder: z.string().optional(), - required: z.boolean().default(false), - disabled: z.boolean().default(false), - description: z.string().optional(), - validation: z.object({ - min: z.union([z.number(), z.string()]).optional(), - max: z.union([z.number(), z.string()]).optional(), - pattern: z.string().optional(), - minLength: z.number().optional(), - maxLength: z.number().optional(), - custom: z.string().optional() - }).optional(), - options: z.array(z.object({ - value: z.string(), - label: z.string(), - disabled: z.boolean().default(false) - })).optional(), - defaultValue: z.any().optional(), - className: z.string().optional(), - grid: z.object({ - span: z.number().min(1).max(12).default(12), - offset: z.number().min(0).max(11).default(0) - }).optional() -}); - -const FormSectionSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - collapsible: z.boolean().default(false), - collapsed: z.boolean().default(false), - fields: z.array(FormFieldSchema) -}); - -// Form configuration schema -const FormConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).default('POST'), - action: z.string().optional(), - enctype: z.enum(['application/x-www-form-urlencoded', 'multipart/form-data', 'text/plain']).default('application/x-www-form-urlencoded'), - sections: z.array(FormSectionSchema), - layout: z.object({ - type: z.enum(['single-column', 'two-column', 'grid', 'card', 'wizard']).default('single-column'), - spacing: z.enum(['compact', 'normal', 'relaxed']).default('normal'), - labelPosition: z.enum(['top', 'left', 'floating']).default('top'), - showProgress: z.boolean().default(false), - showValidation: z.boolean().default(true) - }), - actions: z.object({ - submit: z.object({ - label: z.string().default('Submit'), - variant: z.enum(['primary', 'secondary', 'outline', 'ghost']).default('primary'), - size: z.enum(['sm', 'md', 'lg']).default('md'), - loading: z.boolean().default(false), - disabled: z.boolean().default(false) - }), - cancel: z.object({ - label: z.string().default('Cancel'), - variant: z.enum(['primary', 'secondary', 'outline', 'ghost']).default('outline'), - size: z.enum(['sm', 'md', 'lg']).default('md'), - show: z.boolean().default(false) - }).optional(), - reset: z.object({ - label: z.string().default('Reset'), - variant: z.enum(['primary', 'secondary', 'outline', 'ghost']).default('ghost'), - size: z.enum(['sm', 'md', 'lg']).default('md'), - show: z.boolean().default(false) - }).optional() - }), - validation: z.object({ - mode: z.enum(['onChange', 'onBlur', 'onSubmit']).default('onSubmit'), - showErrors: z.boolean().default(true), - showSuccess: z.boolean().default(false), - customMessages: z.record(z.string()).optional() - }), - styling: z.object({ - theme: z.enum(['light', 'dark', 'auto']).default('auto'), - colors: z.object({ - primary: z.string().default('#3b82f6'), - secondary: z.string().default('#64748b'), - success: z.string().default('#10b981'), - warning: z.string().default('#f59e0b'), - error: z.string().default('#ef4444') - }).optional(), - borderRadius: z.enum(['none', 'sm', 'md', 'lg', 'full']).default('md'), - shadow: z.enum(['none', 'sm', 'md', 'lg', 'xl']).default('sm') - }).optional() -}); - -type FormConfig = z.infer; - -export class FormGenerator implements TemplateGenerator { - name = 'Dynamic Form Generator'; - description = 'Generate comprehensive forms with dynamic fields, validation, and layouts'; - capabilities = [ - 'Dynamic field types', - 'Multi-section forms', - 'Advanced validation', - 'Responsive layouts', - 'Custom styling', - 'Progress tracking', - 'Conditional fields', - 'File uploads', - 'Data binding' - ]; - useCases = [ - 'Contact forms', - 'Registration forms', - 'Survey forms', - 'Order forms', - 'Feedback forms', - 'Application forms', - 'Settings forms', - 'Profile forms', - 'Checkout forms', - 'Multi-step wizards' - ]; - schema = FormConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - // Use custom form data if provided - if (params.customData?.form) { - return this.validateAndEnhanceCustomForm(params.customData.form); - } - - // Generate form based on use case - return this.generateFormByUseCase(params); - } - - async validate(config: FormConfig): Promise { - try { - FormConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [ - await this.generateContactForm(), - await this.generateRegistrationForm(), - await this.generateSurveyForm(), - await this.generateOrderForm() - ]; - } - - private validateAndEnhanceCustomForm(customForm: any): FormConfig { - // Validate the custom form structure - const validated = FormConfigSchema.parse(customForm); - - // Enhance with missing defaults - return { - ...validated, - sections: validated.sections.map(section => ({ - ...section, - fields: section.fields.map(field => ({ - ...field, - grid: field.grid || { span: 12, offset: 0 } - })) - })) - }; - } - - private generateFormByUseCase(params: TemplateGenerationParams): FormConfig { - const useCase = params.useCase?.toLowerCase() || ''; - const title = params.title || 'Dynamic Form'; - - if (useCase.includes('contact')) { - return this.generateContactForm(title); - } else if (useCase.includes('registration') || useCase.includes('signup')) { - return this.generateRegistrationForm(title); - } else if (useCase.includes('survey') || useCase.includes('feedback')) { - return this.generateSurveyForm(title); - } else if (useCase.includes('order') || useCase.includes('checkout')) { - return this.generateOrderForm(title); - } else if (useCase.includes('profile') || useCase.includes('account')) { - return this.generateProfileForm(title); - } else if (useCase.includes('settings')) { - return this.generateSettingsForm(title); - } else if (useCase.includes('application') || useCase.includes('job')) { - return this.generateApplicationForm(title); - } else { - return this.generateGenericForm(title, params); - } - } - - private generateContactForm(title: string = 'Contact Us'): FormConfig { - return { - id: 'contact-form', - title, - description: 'Get in touch with us', - method: 'POST', - action: '/api/contact', - enctype: "application/x-www-form-urlencoded", - sections: [{ - id: 'contact-info', - title: 'Contact Information', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'name', - type: 'text', - label: 'Full Name', - required: false, - disabled: false, - placeholder: 'Enter your full name', - validation: { minLength: 2, maxLength: 100 }, - grid: { span: 6, offset: 0 } - }, - { - id: 'email', - type: 'email', - label: 'Email Address', - required: false, - disabled: false, - placeholder: 'Enter your email', - validation: { pattern: '^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$' }, - grid: { span: 6, offset: 0 } - }, - { - id: 'phone', - type: 'tel', - label: 'Phone Number', - placeholder: 'Enter your phone number', - required: false, - disabled: false, - grid: { span: 6, offset: 0 } - }, - { - id: 'company', - type: 'text', - label: 'Company', - required: false, - disabled: false, - placeholder: 'Enter your company name', - grid: { span: 6, offset: 0 } - }, - // @ts-ignore - - { - id: 'subject', - type: 'select', - label: 'Subject', - required: true, - options: [ - { value: 'general', label: 'General Inquiry', disabled: false }, - { value: 'support', label: 'Technical Support', disabled: false }, - { value: 'sales', label: 'Sales Question', disabled: false }, - { value: 'partnership', label: 'Partnership', disabled: false }, - { value: 'other', label: 'Other', disabled: false } - ], - grid: { span: 12, offset: 0 } - }, - { - id: 'message', - type: 'textarea', - label: 'Message', - required: false, - disabled: false, - placeholder: 'Enter your message', - validation: { minLength: 10, maxLength: 1000 }, - grid: { span: 12, offset: 0 } - } - ] - }], - layout: { - type: 'grid', - spacing: 'normal', - labelPosition: 'top', - showValidation: true, - showProgress: false - }, - actions: { - submit: { - label: 'Send Message', - variant: 'primary', - size: 'md', - disabled: false, - loading: false - }, - reset: { - label: 'Clear Form', - variant: 'outline', - size: 'md', - show: true - } - }, - validation: { - mode: 'onSubmit', - showErrors: true, - showSuccess: true - }, - styling: { - theme: 'auto', - borderRadius: 'md', - shadow: 'sm' - } - }; - } - - private generateRegistrationForm(title: string = 'Create Account'): FormConfig { - return { - id: 'registration-form', - title, - description: 'Join our platform today', - method: 'POST', - action: '/api/register', - enctype: "application/x-www-form-urlencoded", - sections: [ - { - id: 'personal-info', - title: 'Personal Information', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'firstName', - type: 'text', - label: 'First Name', - required: false, - disabled: false, - placeholder: 'Enter your first name', - validation: { minLength: 2, maxLength: 50 }, - grid: { span: 6, offset: 0 } - }, - { - id: 'lastName', - type: 'text', - label: 'Last Name', - required: false, - disabled: false, - placeholder: 'Enter your last name', - validation: { minLength: 2, maxLength: 50 }, - grid: { span: 6, offset: 0 } - }, - { - id: 'email', - type: 'email', - label: 'Email Address', - required: false, - disabled: false, - placeholder: 'Enter your email', - validation: { pattern: '^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$' }, - grid: { span: 12, offset: 0 } - }, - { - id: 'password', - type: 'password', - label: 'Password', - required: false, - disabled: false, - placeholder: 'Create a password', - validation: { minLength: 8, custom: 'Must contain uppercase, lowercase, number, and special character' }, - grid: { span: 6, offset: 0 } - }, - { - id: 'confirmPassword', - type: 'password', - label: 'Confirm Password', - required: false, - disabled: false, - placeholder: 'Confirm your password', - validation: { custom: 'Must match password' }, - grid: { span: 6, offset: 0 } - } - ] - }, - { - id: 'preferences', - title: 'Preferences', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'newsletter', - type: 'checkbox', - required: false, - label: 'Subscribe to newsletter', - defaultValue: true, - disabled: false, - grid: { span: 12, offset: 0 } - }, - { - id: 'terms', - type: 'checkbox', - required: false, - label: 'I agree to the Terms of Service and Privacy Policy', - disabled: false, - grid: { span: 12, offset: 0 } - } - ] - } - ], - layout: { - type: 'card', - spacing: 'normal', - labelPosition: 'top', - showValidation: true, - showProgress: true - }, - actions: { - submit: { - label: 'Create Account', - variant: 'primary', - size: 'lg', - disabled: false, - loading: false - }, - cancel: { - label: 'Already have an account?', - variant: 'ghost', - size: 'md', - show: true - } - }, - validation: { - mode: 'onBlur', - showErrors: true, - showSuccess: true - }, - styling: { - theme: 'auto', - borderRadius: 'lg', - shadow: 'md' - } - }; - } - - private generateSurveyForm(title: string = 'Customer Survey'): FormConfig { - return { - id: 'survey-form', - title, - description: 'Help us improve our services', - method: 'POST', - action: '/api/survey', - enctype: "application/x-www-form-urlencoded", - sections: [ - // @ts-ignore - - { - id: 'experience', - title: 'Your Experience', - collapsible: false, - collapsed: false, - fields: [ - // @ts-ignore - - { - id: 'satisfaction', - type: 'radio', - label: 'How satisfied are you with our service?', - required: true, - options: [ - { value: '5', label: 'Very Satisfied', disabled: false }, - { value: '4', label: 'Satisfied', disabled: false }, - { value: '3', label: 'Neutral', disabled: false }, - { value: '2', label: 'Dissatisfied', disabled: false }, - { value: '1', label: 'Very Dissatisfied', disabled: false } - ], - grid: { span: 12, offset: 0 } - }, - { - id: 'recommend', - type: 'range', - label: 'How likely are you to recommend us? (0-10)', - required: false, - disabled: false, - validation: { min: 0, max: 10 }, - defaultValue: 5, - grid: { span: 12, offset: 0 } - }, - { - id: 'feedback', - type: 'textarea', - label: 'Additional Feedback', - required: false, - disabled: false, - placeholder: 'Tell us what we can improve...', - validation: { maxLength: 500 }, - grid: { span: 12, offset: 0 } - } - ] - } - ], - layout: { - type: 'single-column', - spacing: 'relaxed', - labelPosition: 'top', - showValidation: true, - showProgress: false - }, - actions: { - submit: { - label: 'Submit Survey', - variant: 'primary', - size: 'md', - disabled: false, - loading: false - } - }, - validation: { - mode: 'onChange', - showErrors: true, - showSuccess: false - }, - styling: { - theme: 'auto', - borderRadius: 'md', - shadow: 'sm' - } - }; - } - - private generateOrderForm(title: string = 'Place Order'): FormConfig { - return { - id: 'order-form', - title, - description: 'Complete your purchase', - method: 'POST', - action: '/api/orders', - enctype: 'multipart/form-data', - sections: [ - { - id: 'shipping', - title: 'Shipping Information', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'shippingName', - type: 'text', - label: 'Full Name', - required: true, - disabled: false, - grid: { span: 6, offset: 0 } - }, - { - id: 'shippingPhone', - type: 'tel', - label: 'Phone Number', - required: true, - disabled: false, - grid: { span: 6, offset: 0 } - }, - { - id: 'shippingAddress', - type: 'text', - label: 'Street Address', - required: true, - disabled: false, - grid: { span: 12, offset: 0 } - }, - { - id: 'shippingCity', - type: 'text', - label: 'City', - required: true, - disabled: false, - grid: { span: 4, offset: 0 } - }, - // @ts-ignore - - { - id: 'shippingState', - type: 'select', - label: 'State', - required: true, - options: [ - { value: 'CA', label: 'California', disabled: false }, - { value: 'NY', label: 'New York', disabled: false }, - { value: 'TX', label: 'Texas', disabled: false } - ], - grid: { span: 4, offset: 0 } - }, - { - id: 'shippingZip', - type: 'text', - label: 'ZIP Code', - required: false, - disabled: false, - validation: { pattern: '^\\d{5}(-\\d{4})?$' }, - grid: { span: 4, offset: 0 } - } - ] - }, - { - id: 'payment', - title: 'Payment Information', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'cardNumber', - type: 'text', - label: 'Card Number', - required: false, - disabled: false, - placeholder: '1234 5678 9012 3456', - validation: { pattern: '^\\d{4}\\s\\d{4}\\s\\d{4}\\s\\d{4}$' }, - grid: { span: 12, offset: 0 } - }, - { - id: 'expiryDate', - type: 'text', - label: 'Expiry Date', - required: false, - disabled: false, - placeholder: 'MM/YY', - validation: { pattern: '^\\d{2}/\\d{2}$' }, - grid: { span: 6, offset: 0 } - }, - { - id: 'cvv', - type: 'text', - label: 'CVV', - required: false, - disabled: false, - placeholder: '123', - validation: { pattern: '^\\d{3,4}$' }, - grid: { span: 6, offset: 0 } - } - ] - } - ], - layout: { - type: 'wizard', - spacing: 'normal', - labelPosition: 'top', - showValidation: true, - showProgress: true - }, - actions: { - submit: { - label: 'Place Order', - variant: 'primary', - size: 'lg', - disabled: false, - loading: false - }, - cancel: { - label: 'Cancel', - variant: 'outline', - size: 'md', - show: true - } - }, - validation: { - mode: 'onBlur', - showErrors: true, - showSuccess: false - }, - styling: { - theme: 'auto', - colors: { - primary: '#10b981', - secondary: '#64748b', - success: '#10b981', - warning: '#f59e0b', - error: '#ef4444' - }, - borderRadius: 'md', - shadow: 'lg' - } - }; - } - - private generateProfileForm(title: string = 'Profile Settings'): FormConfig { - return { - id: 'profile-form', - title, - description: 'Update your profile information', - method: 'PUT', - action: '/api/profile', - enctype: 'multipart/form-data', - sections: [{ - id: 'profile-info', - title: 'Profile Information', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'avatar', - type: 'file', - required: false, - label: 'Profile Picture', - description: 'Upload a profile picture (max 5MB)', - disabled: false, - grid: { span: 12, offset: 0 } - }, - { - id: 'bio', - type: 'textarea', - label: 'Bio', - required: false, - disabled: false, - placeholder: 'Tell us about yourself...', - validation: { maxLength: 300 }, - grid: { span: 12, offset: 0 } - }, - { - id: 'website', - type: 'url', - label: 'Website', - required: false, - disabled: false, - placeholder: 'https://example.com', - grid: { span: 6, offset: 0 } - }, - { - id: 'location', - type: 'text', - label: 'Location', - required: false, - disabled: false, - placeholder: 'City, Country', - grid: { span: 6, offset: 0 } - } - ] - }], - layout: { - type: 'single-column', - spacing: 'normal', - labelPosition: 'top', - showValidation: true, - showProgress: false - }, - actions: { - submit: { - label: 'Save Changes', - variant: 'primary', - size: 'md', - disabled: false, - loading: false - }, - reset: { - label: 'Reset', - variant: 'outline', - size: 'md', - show: true - } - }, - validation: { - mode: 'onBlur', - showErrors: true, - showSuccess: true - }, - styling: { - theme: 'auto', - borderRadius: 'md', - shadow: 'sm' - } - }; - } - - private generateSettingsForm(title: string = 'Settings'): FormConfig { - return { - id: 'settings-form', - title, - description: 'Configure your preferences', - method: 'PUT', - action: '/api/settings', - enctype: "application/x-www-form-urlencoded", - sections: [ - { - id: 'notifications', - title: 'Notifications', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'emailNotifications', - type: 'checkbox', - required: false, - label: 'Email notifications', - defaultValue: true, - disabled: false, - grid: { span: 12, offset: 0 } - }, - { - id: 'pushNotifications', - type: 'checkbox', - required: false, - label: 'Push notifications', - defaultValue: false, - disabled: false, - grid: { span: 12, offset: 0 } - } - ] - }, - { - id: 'privacy', - title: 'Privacy', - collapsible: false, - collapsed: false, - fields: [ - // @ts-ignore - - { - id: 'profileVisibility', - type: 'radio', - label: 'Profile visibility', - required: true, - options: [ - { value: 'public', label: 'Public', disabled: false }, - { value: 'friends', label: 'Friends only', disabled: false }, - { value: 'private', label: 'Private', disabled: false } - ], - defaultValue: 'friends', - grid: { span: 12, offset: 0 } - } - ] - } - ], - layout: { - type: 'card', - spacing: 'normal', - labelPosition: 'top', - showValidation: true, - showProgress: false - }, - actions: { - submit: { - label: 'Save Settings', - variant: 'primary', - size: 'md', - disabled: false, - loading: false - } - }, - validation: { - mode: 'onChange', - showErrors: true, - showSuccess: true - }, - styling: { - theme: 'auto', - borderRadius: 'md', - shadow: 'sm' - } - }; - } - - private generateApplicationForm(title: string = 'Job Application'): FormConfig { - return { - id: 'application-form', - title, - description: 'Apply for this position', - method: 'POST', - action: '/api/applications', - enctype: 'multipart/form-data', - sections: [ - { - id: 'personal', - title: 'Personal Information', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'name', - type: 'text', - label: 'Full Name', - required: true, - disabled: false, - grid: { span: 6, offset: 0 } - }, - { - id: 'email', - type: 'email', - label: 'Email', - required: true, - disabled: false, - grid: { span: 6, offset: 0 } - }, - { - id: 'phone', - type: 'tel', - label: 'Phone Number', - required: true, - disabled: false, - grid: { span: 6, offset: 0 } - }, - { - id: 'linkedin', - type: 'url', - label: 'LinkedIn Profile', - required: false, - disabled: false, - placeholder: 'https://linkedin.com/in/username', - grid: { span: 6, offset: 0 } - } - ] - }, - { - id: 'documents', - title: 'Documents', - collapsible: false, - collapsed: false, - fields: [ - { - id: 'resume', - type: 'file', - required: true, - label: 'Resume/CV', - description: 'Upload your resume (PDF preferred)', - disabled: false, - grid: { span: 6, offset: 0 } - }, - { - id: 'coverLetter', - type: 'file', - required: false, - label: 'Cover Letter', - description: 'Optional cover letter', - disabled: false, - grid: { span: 6, offset: 0 } - } - ] - }, - // @ts-ignore - - { - id: 'experience', - title: 'Experience', - collapsible: false, - collapsed: false, - fields: [ - // @ts-ignore - - { - id: 'experience', - type: 'select', - label: 'Years of Experience', - required: true, - options: [ - { value: '0-1', label: '0-1 years', disabled: false }, - { value: '2-3', label: '2-3 years', disabled: false }, - { value: '4-5', label: '4-5 years', disabled: false }, - { value: '6-10', label: '6-10 years', disabled: false }, - { value: '10+', label: '10+ years', disabled: false } - ], - grid: { span: 12, offset: 0 } - }, - { - id: 'motivation', - type: 'textarea', - label: 'Why do you want to work here?', - required: false, - disabled: false, - placeholder: 'Tell us about your motivation...', - validation: { minLength: 50, maxLength: 500 }, - grid: { span: 12, offset: 0 } - } - ] - } - ], - layout: { - type: 'wizard', - spacing: 'normal', - labelPosition: 'top', - showValidation: true, - showProgress: true - }, - actions: { - submit: { - label: 'Submit Application', - variant: 'primary', - size: 'lg', - disabled: false, - loading: false - }, - cancel: { - label: 'Save Draft', - variant: 'outline', - size: 'md', - show: true - } - }, - validation: { - mode: 'onBlur', - showErrors: true, - showSuccess: false - }, - styling: { - theme: 'auto', - borderRadius: 'lg', - shadow: 'md' - } - }; - } - - private generateGenericForm(title: string, params: TemplateGenerationParams): FormConfig { - const fieldCount = Number(params.customData?.fieldCount) || 5; - const fields = []; - - for (let i = 0; i < fieldCount; i++) { - fields.push({ - id: `field_${i + 1}`, - type: 'text' as const, - label: `Field ${i + 1}`, - placeholder: `Enter value for field ${i + 1}`, - required: i < 2, // First 2 fields required - disabled: false, - grid: { span: i % 2 === 0 ? 6 : 6, offset: 0 } - }); - } - - return { - id: 'generic-form', - title, - description: 'A dynamically generated form', - method: 'POST', - action: '/api/submit', - enctype: "application/x-www-form-urlencoded", - sections: [{ - id: 'main-section', - title: 'Form Fields', - collapsible: false, - collapsed: false, - fields - }], - layout: { - type: 'grid', - spacing: 'normal', - labelPosition: 'top', - showValidation: true, - showProgress: false - }, - actions: { - submit: { - label: 'Submit', - variant: 'primary', - size: 'md', - disabled: false, - loading: false - } - }, - validation: { - mode: 'onSubmit', - showErrors: true, - showSuccess: false - }, - styling: { - theme: 'auto', - borderRadius: 'md', - shadow: 'sm' - } - }; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/gallery.ts b/mcp-ui-server-v2/src/templates/generators/gallery.ts deleted file mode 100644 index f1e86c1..0000000 --- a/mcp-ui-server-v2/src/templates/generators/gallery.ts +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Gallery Generator - Dynamic image and media galleries - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const GalleryItemSchema = z.object({ - id: z.string(), - title: z.string().optional(), - description: z.string().optional(), - url: z.string(), - thumbnail: z.string().optional(), - type: z.enum(['image', 'video']).default('image'), - category: z.string().optional(), - tags: z.array(z.string()).default([]), - metadata: z.record(z.any()).optional() -}); - -const GalleryConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - items: z.array(GalleryItemSchema), - layout: z.object({ - type: z.enum(['grid', 'masonry', 'slider', 'lightbox']).default('grid'), - columns: z.number().min(1).max(6).default(3), - spacing: z.enum(['compact', 'normal', 'relaxed']).default('normal') - }), - features: z.object({ - search: z.boolean().default(true), - filter: z.boolean().default(true), - lightbox: z.boolean().default(true), - download: z.boolean().default(false), - share: z.boolean().default(true) - }), - styling: z.object({ - theme: z.enum(['light', 'dark', 'auto']).default('auto'), - borderRadius: z.enum(['none', 'sm', 'md', 'lg']).default('md') - }) -}); - -type GalleryConfig = z.infer; - -export class GalleryGenerator implements TemplateGenerator { - name = 'Gallery Generator'; - description = 'Generate dynamic image and media galleries with various layouts'; - capabilities = ['Grid layouts', 'Lightbox viewing', 'Category filtering', 'Image optimization', 'Responsive design']; - useCases = ['Photo galleries', 'Portfolio showcases', 'Product images', 'Event photos', 'Art collections']; - schema = GalleryConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - if (params.customData?.gallery) { - return GalleryConfigSchema.parse(params.customData.gallery); - } - - return { - id: 'gallery', - title: params.title || 'Photo Gallery', - description: 'A collection of beautiful images', - items: this.generateSampleItems(), - layout: { type: 'grid', columns: 3, spacing: 'normal' }, - features: { search: true, filter: true, lightbox: true, download: false, share: true }, - styling: { theme: 'auto', borderRadius: 'md' } - }; - } - - async validate(config: GalleryConfig): Promise { - try { - GalleryConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [ - { - id: 'photo-gallery', - title: 'Photo Gallery', - items: this.generateSampleItems(), - layout: { type: 'grid', columns: 3, spacing: 'normal' }, - features: { search: true, filter: true, lightbox: true, download: false, share: true }, - styling: { theme: 'auto', borderRadius: 'md' } - } - ]; - } - - private generateSampleItems(): z.infer[] { - return [ - { - id: '1', - title: 'Mountain Landscape', - url: 'https://images.unsplash.com/photo-1506905925346-21bda4d32df4?w=800', - type: 'image', - category: 'nature', - tags: ['mountain', 'landscape', 'nature'] - }, - { - id: '2', - title: 'City Skyline', - url: 'https://images.unsplash.com/photo-1449824913935-59a10b8d2000?w=800', - type: 'image', - category: 'urban', - tags: ['city', 'skyline', 'architecture'] - } - ]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/kanban.ts b/mcp-ui-server-v2/src/templates/generators/kanban.ts deleted file mode 100644 index 801ac15..0000000 --- a/mcp-ui-server-v2/src/templates/generators/kanban.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Kanban Generator - Kanban boards and task management - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const KanbanConfigSchema = z.object({ - id: z.string(), - title: z.string(), - columns: z.array(z.object({ - id: z.string(), - title: z.string(), - color: z.string().optional(), - tasks: z.array(z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - assignee: z.string().optional(), - priority: z.enum(['low', 'medium', 'high']).default('medium') - })).default([]) - })).default([]) -}); - -type KanbanConfig = z.infer; - -export class KanbanGenerator implements TemplateGenerator { - name = 'Kanban Generator'; - description = 'Generate kanban boards and task management interfaces'; - capabilities = ['Task organization', 'Drag and drop', 'Status tracking']; - useCases = ['Project management', 'Task tracking', 'Workflow management']; - schema = KanbanConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'kanban-board', - title: params.title || 'Kanban Board', - columns: [ - { - id: 'todo', - title: 'To Do', - color: '#64748b', - tasks: [ - { id: '1', title: 'Setup project', priority: 'high', assignee: 'John' }, - { id: '2', title: 'Design mockups', priority: 'medium', assignee: 'Jane' } - ] - }, - { - id: 'in-progress', - title: 'In Progress', - color: '#3b82f6', - tasks: [ - { id: '3', title: 'Implement API', priority: 'high', assignee: 'Mike' } - ] - }, - { - id: 'done', - title: 'Done', - color: '#10b981', - tasks: [ - { id: '4', title: 'Project planning', priority: 'medium', assignee: 'Sarah' } - ] - } - ] - }; - } - - async validate(config: KanbanConfig): Promise { - try { - KanbanConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'kanban', title: 'Example Kanban' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/map.ts b/mcp-ui-server-v2/src/templates/generators/map.ts deleted file mode 100644 index 40a4e45..0000000 --- a/mcp-ui-server-v2/src/templates/generators/map.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Map Generator - Maps and location displays - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const MapConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional() -}); - -type MapConfig = z.infer; - -export class MapGenerator implements TemplateGenerator { - name = 'Map Generator'; - description = 'Generate maps and location-based displays'; - capabilities = ['Interactive maps', 'Location markers', 'Geographic data']; - useCases = ['Store locators', 'Event maps', 'Geographic visualization']; - schema = MapConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'location-map', - title: params.title || 'Location Map', - description: 'Interactive map with locations' - }; - } - - async validate(config: MapConfig): Promise { - try { - MapConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'map', title: 'Example Map' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/marketplace.ts b/mcp-ui-server-v2/src/templates/generators/marketplace.ts deleted file mode 100644 index dede651..0000000 --- a/mcp-ui-server-v2/src/templates/generators/marketplace.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Marketplace Generator - Marketplace and listings - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const MarketplaceConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional() -}); - -type MarketplaceConfig = z.infer; - -export class MarketplaceGenerator implements TemplateGenerator { - name = 'Marketplace Generator'; - description = 'Generate marketplace and listing interfaces'; - capabilities = ['Product listings', 'Vendor management', 'Transaction handling']; - useCases = ['Online marketplaces', 'B2B platforms', 'Service directories']; - schema = MarketplaceConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'marketplace', - title: params.title || 'Marketplace', - description: 'Buy and sell products and services' - }; - } - - async validate(config: MarketplaceConfig): Promise { - try { - MarketplaceConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'marketplace', title: 'Example Marketplace' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/portfolio.ts b/mcp-ui-server-v2/src/templates/generators/portfolio.ts deleted file mode 100644 index ea67fa6..0000000 --- a/mcp-ui-server-v2/src/templates/generators/portfolio.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Portfolio Generator - Portfolio and showcase layouts - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const PortfolioConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional() -}); - -type PortfolioConfig = z.infer; - -export class PortfolioGenerator implements TemplateGenerator { - name = 'Portfolio Generator'; - description = 'Generate portfolio and showcase layouts'; - capabilities = ['Project showcases', 'Work displays', 'Creative layouts']; - useCases = ['Designer portfolios', 'Developer showcases', 'Creative professionals', 'Agency websites']; - schema = PortfolioConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'portfolio', - title: params.title || 'Portfolio', - description: 'Showcase your best work' - }; - } - - async validate(config: PortfolioConfig): Promise { - try { - PortfolioConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'portfolio', title: 'Example Portfolio' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/pricing.ts b/mcp-ui-server-v2/src/templates/generators/pricing.ts deleted file mode 100644 index fb28f5b..0000000 --- a/mcp-ui-server-v2/src/templates/generators/pricing.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Pricing Generator - Pricing tables and plans - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const PricingConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional() -}); - -type PricingConfig = z.infer; - -export class PricingGenerator implements TemplateGenerator { - name = 'Pricing Generator'; - description = 'Generate pricing tables and subscription plans'; - capabilities = ['Pricing tiers', 'Feature comparison', 'Plan selection']; - useCases = ['SaaS pricing', 'Service plans', 'Product tiers']; - schema = PricingConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'pricing-table', - title: params.title || 'Pricing Plans', - description: 'Choose the perfect plan for you' - }; - } - - async validate(config: PricingConfig): Promise { - try { - PricingConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'pricing', title: 'Example Pricing' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/product-catalog.ts b/mcp-ui-server-v2/src/templates/generators/product-catalog.ts deleted file mode 100644 index b23b404..0000000 --- a/mcp-ui-server-v2/src/templates/generators/product-catalog.ts +++ /dev/null @@ -1,439 +0,0 @@ -/** - * Product Catalog Generator - Dynamic product listings with filtering and shopping features - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const ProductSchema = z.object({ - id: z.string(), - name: z.string(), - description: z.string(), - price: z.number(), - originalPrice: z.number().optional(), - image: z.string(), - images: z.array(z.string()).optional(), - category: z.string(), - brand: z.string().optional(), - rating: z.number().min(0).max(5).optional(), - reviews: z.number().optional(), - inStock: z.boolean().default(true), - stock: z.number().optional(), - sku: z.string().optional(), - tags: z.array(z.string()).default([]), - variants: z.array(z.object({ - id: z.string(), - name: z.string(), - value: z.string(), - price: z.number().optional(), - inStock: z.boolean().default(true) - })).optional(), - specifications: z.record(z.string()).optional() -}); - -const ProductCatalogConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - products: z.array(ProductSchema), - layout: z.object({ - type: z.enum(['grid', 'list', 'masonry']).default('grid'), - columns: z.number().min(1).max(6).default(3), - spacing: z.enum(['compact', 'normal', 'relaxed']).default('normal'), - cardStyle: z.enum(['minimal', 'detailed', 'hover', 'overlay']).default('detailed') - }), - features: z.object({ - search: z.boolean().default(true), - filters: z.object({ - category: z.boolean().default(true), - price: z.boolean().default(true), - brand: z.boolean().default(true), - rating: z.boolean().default(true), - availability: z.boolean().default(true) - }), - sorting: z.object({ - enabled: z.boolean().default(true), - options: z.array(z.string()).default(['name', 'price', 'rating', 'newest']) - }), - pagination: z.object({ - enabled: z.boolean().default(true), - pageSize: z.number().default(12), - showTotal: z.boolean().default(true) - }), - wishlist: z.boolean().default(true), - compare: z.boolean().default(false), - quickView: z.boolean().default(true) - }), - categories: z.array(z.object({ - id: z.string(), - name: z.string(), - count: z.number().optional(), - image: z.string().optional() - })).default([]), - brands: z.array(z.object({ - id: z.string(), - name: z.string(), - logo: z.string().optional() - })).default([]), - priceRange: z.object({ - min: z.number(), - max: z.number() - }).optional(), - styling: z.object({ - theme: z.enum(['light', 'dark', 'auto']).default('auto'), - primaryColor: z.string().default('#3b82f6'), - borderRadius: z.enum(['none', 'sm', 'md', 'lg']).default('md') - }) -}); - -type ProductCatalogConfig = z.infer; - -export class ProductCatalogGenerator implements TemplateGenerator { - name = 'Product Catalog Generator'; - description = 'Generate dynamic product catalogs with filtering, search, and shopping features'; - capabilities = [ - 'Product grids and lists', - 'Advanced filtering', - 'Search functionality', - 'Price ranges', - 'Category navigation', - 'Product variants', - 'Wishlist support', - 'Quick view modals', - 'Responsive layouts' - ]; - useCases = [ - 'E-commerce stores', - 'Product showcases', - 'Inventory displays', - 'Marketplace listings', - 'Brand catalogs', - 'Wholesale portals', - 'Digital marketplaces', - 'B2B catalogs' - ]; - schema = ProductCatalogConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - if (params.customData?.catalog) { - return ProductCatalogConfigSchema.parse(params.customData.catalog); - } - - return this.generateCatalogByUseCase(params); - } - - async validate(config: ProductCatalogConfig): Promise { - try { - ProductCatalogConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [ - this.generateElectronicsCatalog(), - this.generateFashionCatalog(), - this.generateHomeCatalog() - ]; - } - - private generateCatalogByUseCase(params: TemplateGenerationParams): ProductCatalogConfig { - const useCase = params.useCase?.toLowerCase() || ''; - const title = params.title || 'Product Catalog'; - - if (useCase.includes('electronics') || useCase.includes('tech')) { - return this.generateElectronicsCatalog(title); - } else if (useCase.includes('fashion') || useCase.includes('clothing')) { - return this.generateFashionCatalog(title); - } else if (useCase.includes('home') || useCase.includes('furniture')) { - return this.generateHomeCatalog(title); - } else { - return this.generateGenericCatalog(title); - } - } - - private generateElectronicsCatalog(title: string = 'Electronics Store'): ProductCatalogConfig { - return { - id: 'electronics-catalog', - title, - description: 'Discover the latest in technology and electronics', - products: [ - { - id: 'prod-1', - name: 'Wireless Noise-Canceling Headphones', - description: 'Premium wireless headphones with active noise cancellation', - price: 299.99, - originalPrice: 349.99, - image: 'https://images.unsplash.com/photo-1505740420928-5e560c06d30e?w=400', - category: 'Audio', - brand: 'TechSound', - rating: 4.5, - reviews: 234, - inStock: true, - stock: 15, - sku: 'TS-WH-001', - tags: ['wireless', 'noise-canceling', 'premium'], - variants: [ - { id: 'color-black', name: 'Color', value: 'Black', inStock: true }, - { id: 'color-white', name: 'Color', value: 'White', inStock: true }, - { id: 'color-blue', name: 'Color', value: 'Blue', inStock: false } - ] - }, - { - id: 'prod-2', - name: 'Smartphone Pro Max', - description: 'Latest smartphone with advanced camera system', - price: 999.99, - image: 'https://images.unsplash.com/photo-1592750475338-74b7b21085ab?w=400', - category: 'Mobile', - brand: 'TechPhone', - rating: 4.8, - reviews: 1526, - inStock: true, - stock: 8, - sku: 'TP-PM-128', - tags: ['5G', 'camera', 'premium'] - } - ], - layout: { - type: 'grid', - columns: 3, - spacing: 'normal', - cardStyle: 'detailed' - }, - features: { - search: true, - filters: { - category: true, - price: true, - brand: true, - rating: true, - availability: true - }, - sorting: { - enabled: true, - options: ['name', 'price-low', 'price-high', 'rating', 'newest'] - }, - pagination: { - enabled: true, - pageSize: 12, - showTotal: true - }, - wishlist: true, - compare: true, - quickView: true - }, - categories: [ - { id: 'audio', name: 'Audio', count: 45 }, - { id: 'mobile', name: 'Mobile Devices', count: 32 }, - { id: 'computers', name: 'Computers', count: 78 }, - { id: 'accessories', name: 'Accessories', count: 156 } - ], - brands: [ - { id: 'techsound', name: 'TechSound' }, - { id: 'techphone', name: 'TechPhone' }, - { id: 'compuware', name: 'CompuWare' } - ], - priceRange: { min: 0, max: 2000 }, - styling: { - theme: 'auto', - primaryColor: '#3b82f6', - borderRadius: 'md' - } - }; - } - - private generateFashionCatalog(title: string = 'Fashion Store'): ProductCatalogConfig { - return { - id: 'fashion-catalog', - title, - description: 'Discover the latest fashion trends and styles', - products: [ - { - id: 'fashion-1', - name: 'Premium Cotton T-Shirt', - description: 'Soft, comfortable cotton t-shirt in various colors', - price: 29.99, - originalPrice: 39.99, - image: 'https://images.unsplash.com/photo-1521572163474-6864f9cf17ab?w=400', - category: 'Tops', - brand: 'StyleCo', - rating: 4.3, - reviews: 89, - inStock: true, - stock: 25, - sku: 'SC-CT-001', - tags: ['cotton', 'casual', 'comfortable'], - variants: [ - { id: 'size-s', name: 'Size', value: 'S', inStock: true }, - { id: 'size-m', name: 'Size', value: 'M', inStock: true }, - { id: 'size-l', name: 'Size', value: 'L', inStock: true }, - { id: 'size-xl', name: 'Size', value: 'XL', inStock: false } - ] - } - ], - layout: { - type: 'grid', - columns: 4, - spacing: 'normal', - cardStyle: 'hover' - }, - features: { - search: true, - filters: { - category: true, - price: true, - brand: true, - rating: true, - availability: true - }, - sorting: { - enabled: true, - options: ['newest', 'price-low', 'price-high', 'popular'] - }, - pagination: { - enabled: true, - pageSize: 16, - showTotal: true - }, - wishlist: true, - compare: false, - quickView: true - }, - categories: [ - { id: 'tops', name: 'Tops', count: 124 }, - { id: 'bottoms', name: 'Bottoms', count: 89 }, - { id: 'dresses', name: 'Dresses', count: 67 }, - { id: 'accessories', name: 'Accessories', count: 203 } - ], - brands: [ - { id: 'styleco', name: 'StyleCo' }, - { id: 'fashionhouse', name: 'Fashion House' }, - { id: 'trendy', name: 'Trendy' } - ], - priceRange: { min: 0, max: 300 }, - styling: { - theme: 'auto', - primaryColor: '#ec4899', - borderRadius: 'lg' - } - }; - } - - private generateHomeCatalog(title: string = 'Home & Garden'): ProductCatalogConfig { - return { - id: 'home-catalog', - title, - description: 'Beautiful furniture and home decor for every room', - products: [ - { - id: 'home-1', - name: 'Modern Dining Table', - description: 'Elegant oak dining table for 6 people', - price: 899.99, - image: 'https://images.unsplash.com/photo-1549497538-303791108f95?w=400', - category: 'Furniture', - brand: 'HomeStyle', - rating: 4.7, - reviews: 156, - inStock: true, - stock: 3, - sku: 'HS-DT-001', - tags: ['furniture', 'dining', 'oak', 'modern'] - } - ], - layout: { - type: 'grid', - columns: 3, - spacing: 'relaxed', - cardStyle: 'detailed' - }, - features: { - search: true, - filters: { - category: true, - price: true, - brand: true, - rating: true, - availability: true - }, - sorting: { - enabled: true, - options: ['featured', 'price-low', 'price-high', 'newest'] - }, - pagination: { - enabled: true, - pageSize: 9, - showTotal: true - }, - wishlist: true, - compare: true, - quickView: true - }, - categories: [ - { id: 'furniture', name: 'Furniture', count: 89 }, - { id: 'decor', name: 'Home Decor', count: 234 }, - { id: 'lighting', name: 'Lighting', count: 67 }, - { id: 'textiles', name: 'Textiles', count: 145 } - ], - brands: [ - { id: 'homestyle', name: 'HomeStyle' }, - { id: 'comfort', name: 'Comfort Living' }, - { id: 'modern', name: 'Modern Home' } - ], - priceRange: { min: 0, max: 5000 }, - styling: { - theme: 'auto', - primaryColor: '#059669', - borderRadius: 'md' - } - }; - } - - private generateGenericCatalog(title: string): ProductCatalogConfig { - return { - id: 'generic-catalog', - title, - description: 'Browse our collection of products', - products: [], - layout: { - type: 'grid', - columns: 3, - spacing: 'normal', - cardStyle: 'detailed' - }, - features: { - search: true, - filters: { - category: true, - price: true, - brand: false, - rating: true, - availability: true - }, - sorting: { - enabled: true, - options: ['name', 'price', 'newest'] - }, - pagination: { - enabled: true, - pageSize: 12, - showTotal: true - }, - wishlist: false, - compare: false, - quickView: false - }, - categories: [], - brands: [], - styling: { - theme: 'auto', - primaryColor: '#3b82f6', - borderRadius: 'md' - } - }; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/profile-card.ts b/mcp-ui-server-v2/src/templates/generators/profile-card.ts deleted file mode 100644 index ffb05fb..0000000 --- a/mcp-ui-server-v2/src/templates/generators/profile-card.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Profile Card Generator - User profiles and cards - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const ProfileCardConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional() -}); - -type ProfileCardConfig = z.infer; - -export class ProfileCardGenerator implements TemplateGenerator { - name = 'Profile Card Generator'; - description = 'Generate user profile cards and displays'; - capabilities = ['User profiles', 'Avatar display', 'Contact information']; - useCases = ['Team pages', 'User directories', 'Contact cards']; - schema = ProfileCardConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'profile-card', - title: params.title || 'Profile Card', - description: 'User profile information' - }; - } - - async validate(config: ProfileCardConfig): Promise { - try { - ProfileCardConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'profileCard', title: 'Example Profile' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/sample-data.ts b/mcp-ui-server-v2/src/templates/generators/sample-data.ts deleted file mode 100644 index 26e7120..0000000 --- a/mcp-ui-server-v2/src/templates/generators/sample-data.ts +++ /dev/null @@ -1,565 +0,0 @@ -/** - * Sample Data Generator - Generate realistic test data for templates - */ - -import { z } from 'zod'; - -// Data type definitions -const SampleDataSchema = z.object({ - type: z.enum(['users', 'products', 'orders', 'companies', 'events', 'articles', 'comments', 'transactions', 'locations', 'tasks']), - count: z.number().min(1).max(1000).default(10), - seed: z.string().optional(), - options: z.record(z.any()).optional() -}); - -export class SampleDataGenerator { - private userNames = [ - 'John Doe', 'Jane Smith', 'Mike Johnson', 'Sarah Wilson', 'David Brown', - 'Emily Davis', 'Chris Anderson', 'Lisa Garcia', 'Tom Martinez', 'Amy Taylor', - 'Robert Lee', 'Jennifer White', 'Mark Thompson', 'Michelle Clark', 'Paul Rodriguez' - ]; - - private companies = [ - 'TechCorp', 'DataSys Inc', 'CloudWorks', 'InnovateLab', 'DigitalFlow', - 'NextGen Solutions', 'SmartTech', 'FutureSoft', 'CodeCraft', 'ByteBuilders' - ]; - - private productCategories = ['electronics', 'clothing', 'books', 'home', 'sports', 'beauty', 'automotive', 'toys']; - - private cities = [ - 'New York', 'Los Angeles', 'Chicago', 'Houston', 'Phoenix', 'Philadelphia', - 'San Antonio', 'San Diego', 'Dallas', 'San Jose', 'Austin', 'Jacksonville' - ]; - - private articleTitles = [ - 'The Future of Technology', - 'Best Practices for Development', - 'Understanding User Experience', - 'Market Trends Analysis', - 'Innovation in Business', - 'Sustainable Solutions', - 'Digital Transformation', - 'AI and Machine Learning' - ]; - - async generate(type: string, count: number = 10, seed?: string): Promise { - // Set random seed if provided - if (seed) { - this.setSeed(seed); - } - - switch (type) { - case 'users': - return this.generateUsers(count); - case 'products': - return this.generateProducts(count); - case 'orders': - return this.generateOrders(count); - case 'companies': - return this.generateCompanies(count); - case 'events': - return this.generateEvents(count); - case 'articles': - return this.generateArticles(count); - case 'comments': - return this.generateComments(count); - case 'transactions': - return this.generateTransactions(count); - case 'locations': - return this.generateLocations(count); - case 'tasks': - return this.generateTasks(count); - default: - throw new Error(`Unknown data type: ${type}`); - } - } - - private generateUsers(count: number) { - const users = []; - const roles = ['admin', 'editor', 'user', 'guest']; - const statuses = ['active', 'inactive', 'suspended']; - - for (let i = 0; i < count; i++) { - const name = this.randomChoice(this.userNames); - const email = `${name.toLowerCase().replace(' ', '.')}@example.com`; - - users.push({ - id: `user_${i + 1}`, - name, - email, - avatar: `https://images.unsplash.com/photo-${this.randomInt(1400000000000, 1700000000000)}?w=150&h=150&fit=crop&crop=face`, - role: this.randomChoice(roles), - status: this.randomChoice(statuses), - lastLogin: this.randomPastDate(30), - createdAt: this.randomPastDate(365), - phone: this.generatePhoneNumber(), - location: this.randomChoice(this.cities), - bio: `${name} is a professional with expertise in their field.`, - website: `https://${name.toLowerCase().replace(' ', '')}.com` - }); - } - - return users; - } - - private generateProducts(count: number) { - const products = []; - const statuses = ['active', 'draft', 'out-of-stock']; - - for (let i = 0; i < count; i++) { - const category = this.randomChoice(this.productCategories); - const name = this.generateProductName(category); - - products.push({ - id: `product_${i + 1}`, - name, - sku: `SKU-${this.randomString(6).toUpperCase()}`, - description: `High-quality ${name.toLowerCase()} with excellent features.`, - category, - price: this.randomFloat(9.99, 999.99), - originalPrice: this.randomFloat(19.99, 1299.99), - stock: this.randomInt(0, 100), - rating: this.randomFloat(3.0, 5.0), - reviews: this.randomInt(0, 500), - image: `https://images.unsplash.com/photo-${this.randomInt(1400000000000, 1700000000000)}?w=300&h=300&fit=crop`, - status: this.randomChoice(statuses), - tags: this.generateTags(category), - weight: this.randomFloat(0.1, 10.0), - dimensions: { - length: this.randomFloat(5, 50), - width: this.randomFloat(5, 50), - height: this.randomFloat(1, 20) - } - }); - } - - return products; - } - - private generateOrders(count: number) { - const orders = []; - const statuses = ['pending', 'processing', 'shipped', 'delivered', 'cancelled']; - - for (let i = 0; i < count; i++) { - const orderNumber = `ORD-${String(i + 1).padStart(6, '0')}`; - const items = this.randomInt(1, 5); - const subtotal = this.randomFloat(25.00, 500.00); - const tax = subtotal * 0.08; - const shipping = items > 2 ? 0 : 9.99; - const total = subtotal + tax + shipping; - - orders.push({ - id: `order_${i + 1}`, - orderNumber, - customer: this.randomChoice(this.userNames), - email: `customer${i + 1}@example.com`, - status: this.randomChoice(statuses), - date: this.randomPastDate(90), - items, - subtotal: Math.round(subtotal * 100) / 100, - tax: Math.round(tax * 100) / 100, - shipping: shipping, - total: Math.round(total * 100) / 100, - paymentMethod: this.randomChoice(['credit_card', 'paypal', 'bank_transfer']), - shippingAddress: { - street: `${this.randomInt(100, 9999)} Main St`, - city: this.randomChoice(this.cities), - state: 'CA', - zip: String(this.randomInt(10000, 99999)), - country: 'USA' - }, - trackingNumber: this.randomChoice(statuses) === 'shipped' || this.randomChoice(statuses) === 'delivered' - ? `TRK${this.randomString(10).toUpperCase()}` - : null - }); - } - - return orders; - } - - private generateCompanies(count: number) { - const companies = []; - const industries = ['Technology', 'Healthcare', 'Finance', 'Education', 'Retail', 'Manufacturing']; - const sizes = ['Startup', 'Small', 'Medium', 'Large', 'Enterprise']; - - for (let i = 0; i < count; i++) { - const name = this.randomChoice(this.companies); - - companies.push({ - id: `company_${i + 1}`, - name, - industry: this.randomChoice(industries), - size: this.randomChoice(sizes), - employees: this.randomInt(10, 10000), - founded: this.randomInt(1990, 2020), - revenue: this.randomFloat(100000, 100000000), - website: `https://${name.toLowerCase().replace(/\s+/g, '')}.com`, - logo: `https://images.unsplash.com/photo-${this.randomInt(1400000000000, 1700000000000)}?w=200&h=200&fit=crop`, - location: { - city: this.randomChoice(this.cities), - state: 'CA', - country: 'USA' - }, - description: `${name} is a leading company in the ${this.randomChoice(industries).toLowerCase()} industry.`, - contacts: { - phone: this.generatePhoneNumber(), - email: `info@${name.toLowerCase().replace(/\s+/g, '')}.com` - } - }); - } - - return companies; - } - - private generateEvents(count: number) { - const events = []; - const types = ['conference', 'workshop', 'webinar', 'meeting', 'training']; - const statuses = ['upcoming', 'ongoing', 'completed', 'cancelled']; - - for (let i = 0; i < count; i++) { - const startDate = this.randomFutureDate(180); - const endDate = new Date(startDate.getTime() + this.randomInt(1, 8) * 60 * 60 * 1000); - - events.push({ - id: `event_${i + 1}`, - title: `${this.randomChoice(['Tech', 'Business', 'Design', 'Marketing'])} ${this.randomChoice(['Summit', 'Conference', 'Workshop', 'Meetup'])} ${new Date().getFullYear()}`, - description: 'Join us for an exciting event featuring industry experts and networking opportunities.', - type: this.randomChoice(types), - status: this.randomChoice(statuses), - startDate, - endDate, - location: this.randomChoice(this.cities), - venue: 'Convention Center', - capacity: this.randomInt(50, 1000), - attendees: this.randomInt(10, 800), - price: this.randomFloat(0, 299.99), - organizer: this.randomChoice(this.userNames), - tags: this.generateEventTags(), - image: `https://images.unsplash.com/photo-${this.randomInt(1400000000000, 1700000000000)}?w=400&h=300&fit=crop` - }); - } - - return events; - } - - private generateArticles(count: number) { - const articles = []; - const categories = ['Technology', 'Business', 'Design', 'Marketing', 'Science']; - const statuses = ['published', 'draft', 'archived']; - - for (let i = 0; i < count; i++) { - const title = this.randomChoice(this.articleTitles); - - articles.push({ - id: `article_${i + 1}`, - title, - slug: title.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''), - content: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.', - excerpt: 'Brief summary of the article content...', - category: this.randomChoice(categories), - status: this.randomChoice(statuses), - author: this.randomChoice(this.userNames), - publishedAt: this.randomPastDate(365), - updatedAt: this.randomPastDate(30), - views: this.randomInt(100, 10000), - likes: this.randomInt(10, 500), - comments: this.randomInt(0, 50), - readTime: this.randomInt(2, 15), - featuredImage: `https://images.unsplash.com/photo-${this.randomInt(1400000000000, 1700000000000)}?w=600&h=400&fit=crop`, - tags: this.generateArticleTags(), - seo: { - metaTitle: title, - metaDescription: 'SEO-optimized description for the article.', - keywords: this.generateArticleTags().join(', ') - } - }); - } - - return articles; - } - - private generateComments(count: number) { - const comments = []; - - for (let i = 0; i < count; i++) { - comments.push({ - id: `comment_${i + 1}`, - content: this.generateCommentContent(), - author: this.randomChoice(this.userNames), - email: `commenter${i + 1}@example.com`, - createdAt: this.randomPastDate(90), - updatedAt: this.randomPastDate(7), - likes: this.randomInt(0, 50), - replies: this.randomInt(0, 10), - approved: this.randomBoolean(0.9), - parentId: this.randomBoolean(0.3) ? `comment_${this.randomInt(1, i)}` : null, - avatar: `https://images.unsplash.com/photo-${this.randomInt(1400000000000, 1700000000000)}?w=80&h=80&fit=crop&crop=face` - }); - } - - return comments; - } - - private generateTransactions(count: number) { - const transactions = []; - const types = ['income', 'expense']; - const categories = ['salary', 'freelance', 'investment', 'rent', 'utilities', 'food', 'transport', 'entertainment']; - const statuses = ['completed', 'pending', 'failed']; - - for (let i = 0; i < count; i++) { - const type = this.randomChoice(types); - const amount = type === 'income' ? this.randomFloat(100, 5000) : this.randomFloat(10, 1000); - - transactions.push({ - id: `txn_${i + 1}`, - type, - amount: Math.round(amount * 100) / 100, - currency: 'USD', - category: this.randomChoice(categories), - description: `${type === 'income' ? 'Payment received' : 'Payment made'} for ${this.randomChoice(categories)}`, - status: this.randomChoice(statuses), - date: this.randomPastDate(365), - reference: `REF${this.randomString(8).toUpperCase()}`, - paymentMethod: this.randomChoice(['credit_card', 'bank_transfer', 'cash', 'paypal']), - merchant: this.randomChoice(this.companies), - tags: this.generateTransactionTags(type), - location: this.randomChoice(this.cities) - }); - } - - return transactions; - } - - private generateLocations(count: number) { - const locations = []; - const types = ['office', 'store', 'warehouse', 'restaurant', 'hotel']; - - for (let i = 0; i < count; i++) { - locations.push({ - id: `location_${i + 1}`, - name: `${this.randomChoice(['Central', 'Downtown', 'West', 'East', 'North'])} ${this.randomChoice(types)}`, - type: this.randomChoice(types), - address: { - street: `${this.randomInt(100, 9999)} ${this.randomChoice(['Main', 'Oak', 'Pine', 'First', 'Second'])} St`, - city: this.randomChoice(this.cities), - state: 'CA', - zip: String(this.randomInt(10000, 99999)), - country: 'USA' - }, - coordinates: { - lat: this.randomFloat(32.0, 42.0), - lng: this.randomFloat(-124.0, -114.0) - }, - phone: this.generatePhoneNumber(), - email: `contact@location${i + 1}.com`, - hours: { - monday: '9:00 AM - 6:00 PM', - tuesday: '9:00 AM - 6:00 PM', - wednesday: '9:00 AM - 6:00 PM', - thursday: '9:00 AM - 6:00 PM', - friday: '9:00 AM - 6:00 PM', - saturday: '10:00 AM - 4:00 PM', - sunday: 'Closed' - }, - rating: this.randomFloat(3.0, 5.0), - reviews: this.randomInt(10, 200) - }); - } - - return locations; - } - - private generateTasks(count: number) { - const tasks = []; - const statuses = ['todo', 'in-progress', 'review', 'done']; - const priorities = ['low', 'medium', 'high', 'urgent']; - const types = ['bug', 'feature', 'improvement', 'documentation']; - - for (let i = 0; i < count; i++) { - const createdDate = this.randomPastDate(90); - const dueDate = this.randomFutureDate(30); - - tasks.push({ - id: `task_${i + 1}`, - title: `${this.randomChoice(['Fix', 'Implement', 'Update', 'Create', 'Review'])} ${this.randomChoice(['user interface', 'database', 'API', 'documentation', 'tests'])}`, - description: 'Detailed description of the task requirements and acceptance criteria.', - status: this.randomChoice(statuses), - priority: this.randomChoice(priorities), - type: this.randomChoice(types), - assignee: this.randomChoice(this.userNames), - reporter: this.randomChoice(this.userNames), - createdAt: createdDate, - updatedAt: this.randomPastDate(7), - dueDate, - estimatedHours: this.randomInt(1, 40), - actualHours: this.randomInt(0, 50), - tags: this.generateTaskTags(), - project: `Project ${this.randomChoice(['Alpha', 'Beta', 'Gamma', 'Delta'])}`, - milestone: `Release ${this.randomFloat(1.0, 3.0).toFixed(1)}`, - comments: this.randomInt(0, 15), - attachments: this.randomInt(0, 5) - }); - } - - return tasks; - } - - // Helper methods - private randomInt(min: number, max: number): number { - return Math.floor(Math.random() * (max - min + 1)) + min; - } - - private randomFloat(min: number, max: number): number { - return Math.random() * (max - min) + min; - } - - private randomChoice(array: T[]): T { - if (array.length === 0) { - throw new Error('Cannot choose from empty array'); - } - const result = array[Math.floor(Math.random() * array.length)]; - if (result === undefined) { - throw new Error('Selected undefined value from array'); - } - return result; - } - - private randomBoolean(probability: number = 0.5): boolean { - return Math.random() < probability; - } - - private randomString(length: number): string { - const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; - let result = ''; - for (let i = 0; i < length; i++) { - result += chars.charAt(Math.floor(Math.random() * chars.length)); - } - return result; - } - - private randomPastDate(daysAgo: number): Date { - const now = new Date(); - const past = new Date(now.getTime() - (Math.random() * daysAgo * 24 * 60 * 60 * 1000)); - return past; - } - - private randomFutureDate(daysFromNow: number): Date { - const now = new Date(); - const future = new Date(now.getTime() + (Math.random() * daysFromNow * 24 * 60 * 60 * 1000)); - return future; - } - - private generatePhoneNumber(): string { - const area = this.randomInt(200, 999); - const exchange = this.randomInt(200, 999); - const number = this.randomInt(1000, 9999); - return `(${area}) ${exchange}-${number}`; - } - - private generateProductName(category: string): string { - const adjectives = ['Premium', 'Professional', 'Ultimate', 'Advanced', 'Pro', 'Elite', 'Smart', 'Modern']; - const electronics = ['Headphones', 'Smartphone', 'Laptop', 'Tablet', 'Speaker', 'Camera', 'Monitor']; - const clothing = ['T-Shirt', 'Jeans', 'Jacket', 'Sneakers', 'Dress', 'Sweater', 'Coat']; - const books = ['Guide', 'Manual', 'Handbook', 'Encyclopedia', 'Novel', 'Biography', 'Textbook']; - const home = ['Chair', 'Table', 'Lamp', 'Rug', 'Mirror', 'Vase', 'Clock']; - - const adj = this.randomChoice(adjectives); - let noun = ''; - - switch (category) { - case 'electronics': - noun = this.randomChoice(electronics); - break; - case 'clothing': - noun = this.randomChoice(clothing); - break; - case 'books': - noun = this.randomChoice(books); - break; - case 'home': - noun = this.randomChoice(home); - break; - default: - noun = 'Product'; - } - - return `${adj} ${noun}`; - } - - private generateTags(category: string): string[] { - const baseTags = ['featured', 'popular', 'new']; - const categoryTags: Record = { - electronics: ['wireless', 'bluetooth', 'portable', 'gaming'], - clothing: ['cotton', 'comfortable', 'stylish', 'casual'], - books: ['bestseller', 'educational', 'fiction', 'non-fiction'], - home: ['modern', 'decorative', 'functional', 'minimalist'] - }; - - const tags = [...baseTags]; - const specific = categoryTags[category] || []; - tags.push(...this.randomChoice(specific.slice(0, 2))); - - return tags; - } - - private generateEventTags(): string[] { - const tags = ['networking', 'learning', 'professional', 'industry', 'innovation']; - return tags.slice(0, this.randomInt(2, 4)); - } - - private generateArticleTags(): string[] { - const tags = ['tutorial', 'guide', 'tips', 'best-practices', 'industry-news', 'analysis']; - return tags.slice(0, this.randomInt(2, 5)); - } - - private generateTransactionTags(type: string): string[] { - const incomeTags = ['work', 'business', 'investment', 'bonus']; - const expenseTags = ['essential', 'luxury', 'monthly', 'one-time']; - - const baseTags = type === 'income' ? incomeTags : expenseTags; - return [this.randomChoice(baseTags)]; - } - - private generateTaskTags(): string[] { - const tags = ['backend', 'frontend', 'urgent', 'enhancement', 'refactor']; - return tags.slice(0, this.randomInt(1, 3)); - } - - private generateCommentContent(): string { - const contents = [ - 'Great article! Very informative and well-written.', - 'I found this really helpful. Thanks for sharing!', - 'Interesting perspective. I hadn\'t thought about it that way.', - 'Could you provide more details about this topic?', - 'This is exactly what I was looking for. Much appreciated!', - 'I have a different opinion on this matter...', - 'Excellent points! Looking forward to more content like this.', - 'Very well explained. Easy to understand and follow.' - ]; - return this.randomChoice(contents); - } - - private setSeed(seed: string): void { - // Simple seed implementation - in production, use a proper PRNG - let hash = 0; - for (let i = 0; i < seed.length; i++) { - const char = seed.charCodeAt(i); - hash = ((hash << 5) - hash) + char; - hash = hash & hash; // Convert to 32bit integer - } - - // Override Math.random with seeded version - const originalRandom = Math.random; - let seedValue = Math.abs(hash); - - Math.random = () => { - seedValue = (seedValue * 9301 + 49297) % 233280; - return seedValue / 233280; - }; - - // Restore original after a short delay (this is a simple implementation) - setTimeout(() => { - Math.random = originalRandom; - }, 100); - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/stats.ts b/mcp-ui-server-v2/src/templates/generators/stats.ts deleted file mode 100644 index 9faf709..0000000 --- a/mcp-ui-server-v2/src/templates/generators/stats.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Stats Generator - Statistics and KPI displays - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const StatsConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - stats: z.array(z.object({ - id: z.string(), - label: z.string(), - value: z.union([z.string(), z.number()]), - icon: z.string().optional(), - trend: z.object({ - direction: z.enum(['up', 'down', 'stable']).default('stable'), - percentage: z.number().optional() - }).optional() - })).default([]) -}); - -type StatsConfig = z.infer; - -export class StatsGenerator implements TemplateGenerator { - name = 'Stats Generator'; - description = 'Generate statistics and KPI displays'; - capabilities = ['Key metrics', 'Performance indicators', 'Trend visualization']; - useCases = ['Dashboard widgets', 'Performance tracking', 'Business metrics', 'Analytics']; - schema = StatsConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'key-stats', - title: params.title || 'Key Statistics', - description: 'Important metrics at a glance', - stats: [ - { - id: 'revenue', - label: 'Total Revenue', - value: '$125,430', - icon: 'DollarSign', - trend: { direction: 'up', percentage: 12.5 } - }, - { - id: 'users', - label: 'Active Users', - value: '2,458', - icon: 'Users', - trend: { direction: 'up', percentage: 8.2 } - } - ] - }; - } - - async validate(config: StatsConfig): Promise { - try { - StatsConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'stats', title: 'Example Stats' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/timeline.ts b/mcp-ui-server-v2/src/templates/generators/timeline.ts deleted file mode 100644 index d7682dd..0000000 --- a/mcp-ui-server-v2/src/templates/generators/timeline.ts +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Timeline Generator - Timeline and chronological views - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const TimelineConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - events: z.array(z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional(), - date: z.string(), - type: z.enum(['milestone', 'event', 'achievement']).default('event'), - icon: z.string().optional() - })).default([]) -}); - -type TimelineConfig = z.infer; - -export class TimelineGenerator implements TemplateGenerator { - name = 'Timeline Generator'; - description = 'Generate timeline and chronological views'; - capabilities = ['Chronological display', 'Event tracking', 'Progress visualization']; - useCases = ['Project timelines', 'Company history', 'Process flows', 'Event history']; - schema = TimelineConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'project-timeline', - title: params.title || 'Project Timeline', - description: 'Key milestones and events', - events: [ - { - id: '1', - title: 'Project Started', - description: 'Initial project kickoff and team formation', - date: '2024-01-01', - type: 'milestone', - icon: 'Play' - }, - { - id: '2', - title: 'MVP Released', - description: 'First version of the product launched', - date: '2024-03-15', - type: 'achievement', - icon: 'Award' - } - ] - }; - } - - async validate(config: TimelineConfig): Promise { - try { - TimelineConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'timeline', title: 'Example Timeline' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/templates/generators/wizard.ts b/mcp-ui-server-v2/src/templates/generators/wizard.ts deleted file mode 100644 index a105e9d..0000000 --- a/mcp-ui-server-v2/src/templates/generators/wizard.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Wizard Generator - Multi-step wizards and flows - */ - -import { z } from 'zod'; -import type { TemplateGenerator } from '../engine.js'; -import type { TemplateGenerationParams } from '../../types/mcp.js'; - -const WizardConfigSchema = z.object({ - id: z.string(), - title: z.string(), - description: z.string().optional() -}); - -type WizardConfig = z.infer; - -export class WizardGenerator implements TemplateGenerator { - name = 'Wizard Generator'; - description = 'Generate multi-step wizards and guided flows'; - capabilities = ['Step-by-step flows', 'Progress tracking', 'Form validation']; - useCases = ['Onboarding', 'Setup processes', 'Data collection']; - schema = WizardConfigSchema; - - async generate(params: TemplateGenerationParams): Promise { - return { - id: 'setup-wizard', - title: params.title || 'Setup Wizard', - description: 'Complete the setup process step by step' - }; - } - - async validate(config: WizardConfig): Promise { - try { - WizardConfigSchema.parse(config); - return true; - } catch { - return false; - } - } - - async getExamples(): Promise { - return [await this.generate({ templateType: 'wizard', title: 'Example Wizard' })]; - } -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/tools/manager.ts b/mcp-ui-server-v2/src/tools/manager.ts deleted file mode 100644 index 28eb268..0000000 --- a/mcp-ui-server-v2/src/tools/manager.ts +++ /dev/null @@ -1,465 +0,0 @@ -/** - * Tool Manager for MCP Server - * Manages all available tools and their execution - */ - -import { z } from 'zod'; -import type { Tool, ToolResult, TemplateGenerationParams, JSONSchema } from '../types/mcp.js'; -import { getLogger } from '../core/logger.js'; -import { getCache } from '../core/cache.js'; -import { TemplateEngine } from '../templates/engine.js'; -import { ValidationError, ToolExecutionError, ErrorUtils } from '../utils/errors.js'; - -export class ToolManager { - private tools = new Map(); - private logger = getLogger(); - private cache = getCache(); - private templateEngine: TemplateEngine; - - constructor() { - this.templateEngine = new TemplateEngine(); - } - - async initialize(): Promise { - try { - await this.registerCoreTools(); - await this.templateEngine.initialize(); - - this.logger.info('Tool Manager initialized', { - toolCount: this.tools.size, - tools: Array.from(this.tools.keys()), - }); - } catch (error) { - this.logger.error('Failed to initialize Tool Manager', error); - throw error; - } - } - - /** - * Register all core tools - */ - private async registerCoreTools(): Promise { - // Template generation tools - this.registerTool({ - name: 'generate_template', - description: 'Generate a dynamic UI template with comprehensive customization options', - inputSchema: this.createTemplateGenerationSchema(), - handler: this.handleGenerateTemplate.bind(this), - }); - - this.registerTool({ - name: 'list_template_types', - description: 'List all available template types with their capabilities', - inputSchema: { type: 'object', properties: {} }, - handler: this.handleListTemplateTypes.bind(this), - }); - - this.registerTool({ - name: 'get_template_schema', - description: 'Get the JSON schema for a specific template type', - inputSchema: { - type: 'object', - properties: { - templateType: { - type: 'string', - enum: [ - 'dashboard', 'dataTable', 'productCatalog', 'profileCard', 'timeline', - 'gallery', 'pricing', 'stats', 'calendar', 'wizard', 'chart', 'map', - 'kanban', 'feed', 'form', 'marketplace', 'analytics', 'ecommerce', - 'blog', 'portfolio' - ], - description: 'The template type to get schema for' - } - }, - required: ['templateType'] - }, - handler: this.handleGetTemplateSchema.bind(this), - }); - - this.registerTool({ - name: 'validate_template', - description: 'Validate a template configuration against its schema', - inputSchema: { - type: 'object', - properties: { - templateType: { - type: 'string', - enum: [ - 'dashboard', 'dataTable', 'productCatalog', 'profileCard', 'timeline', - 'gallery', 'pricing', 'stats', 'calendar', 'wizard', 'chart', 'map', - 'kanban', 'feed', 'form', 'marketplace', 'analytics', 'ecommerce', - 'blog', 'portfolio' - ] - }, - config: { - type: 'object', - description: 'Template configuration to validate' - } - }, - required: ['templateType', 'config'] - }, - handler: this.handleValidateTemplate.bind(this), - }); - - // Data and utility tools - this.registerTool({ - name: 'generate_sample_data', - description: 'Generate realistic sample data for templates', - inputSchema: { - type: 'object', - properties: { - dataType: { - type: 'string', - enum: ['users', 'products', 'metrics', 'events', 'posts', 'projects'], - description: 'Type of sample data to generate' - }, - count: { - type: 'number', - minimum: 1, - maximum: 1000, - default: 10, - description: 'Number of items to generate' - }, - seed: { - type: 'string', - description: 'Seed for reproducible data generation' - } - }, - required: ['dataType'] - }, - handler: this.handleGenerateSampleData.bind(this), - }); - - this.registerTool({ - name: 'cache_stats', - description: 'Get cache statistics and performance metrics', - inputSchema: { type: 'object', properties: {} }, - handler: this.handleCacheStats.bind(this), - }); - - this.registerTool({ - name: 'clear_cache', - description: 'Clear template and data cache', - inputSchema: { - type: 'object', - properties: { - pattern: { - type: 'string', - description: 'Cache key pattern to clear (optional)' - } - } - }, - handler: this.handleClearCache.bind(this), - }); - } - - /** - * Register a new tool - */ - registerTool(definition: ToolDefinition): void { - this.tools.set(definition.name, definition); - this.logger.debug('Tool registered', { toolName: definition.name }); - } - - /** - * List all available tools - */ - async listTools(): Promise { - return Array.from(this.tools.values()).map(def => ({ - name: def.name, - description: def.description, - inputSchema: def.inputSchema, - })); - } - - /** - * Call a tool with the given arguments - */ - async callTool(name: string, args: Record): Promise { - const tool = this.tools.get(name); - if (!tool) { - throw new ToolExecutionError(name, `Tool not found: ${name}`); - } - - try { - // Validate arguments against schema - this.validateArgs(args, tool.inputSchema, name); - - // Execute the tool - const result = await tool.handler(args); - - return { - content: [{ - type: 'text', - text: typeof result === 'string' ? result : JSON.stringify(result, null, 2) - }], - isError: false, - }; - } catch (error) { - this.logger.error(`Tool execution failed: ${name}`, error, { args }); - - if (error instanceof ValidationError) { - throw error; - } - - throw new ToolExecutionError(name, error instanceof Error ? error.message : 'Unknown error'); - } - } - - /** - * Validate tool arguments against schema - */ - private validateArgs(args: Record, schema: JSONSchema, toolName: string): void { - try { - // Convert JSON Schema to Zod schema for validation - const zodSchema = this.jsonSchemaToZod(schema); - zodSchema.parse(args); - } catch (error) { - if (error instanceof z.ZodError) { - throw ErrorUtils.fromZodError(error); - } - throw new ValidationError(`Invalid arguments for tool ${toolName}`); - } - } - - /** - * Convert JSON Schema to Zod schema (simplified version) - */ - private jsonSchemaToZod(schema: JSONSchema): z.ZodSchema { - if (schema.type === 'object') { - const shape: Record = {}; - - if (schema.properties) { - for (const [key, propSchema] of Object.entries(schema.properties)) { - let zodProp = this.jsonSchemaToZod(propSchema); - - // Make optional if not in required array - if (!schema.required?.includes(key)) { - zodProp = zodProp.optional(); - } - - shape[key] = zodProp; - } - } - - return z.object(shape); - } - - if (schema.type === 'string') { - if (schema.enum) { - return z.enum(schema.enum as [string, ...string[]]); - } - - return z.string(); - } - - if (schema.type === 'number') { - let zodNumber = z.number(); - - if (schema.minimum !== undefined) { - zodNumber = zodNumber.min(schema.minimum); - } - - if (schema.maximum !== undefined) { - zodNumber = zodNumber.max(schema.maximum); - } - - return zodNumber; - } - - if (schema.type === 'boolean') { - return z.boolean(); - } - - if (schema.type === 'array') { - const itemSchema = schema.items ? this.jsonSchemaToZod(schema.items) : z.unknown(); - return z.array(itemSchema); - } - - return z.unknown(); - } - - // Tool Handlers - - private async handleGenerateTemplate(args: Record): Promise { - const params = args as unknown as TemplateGenerationParams; - - // Check cache first - const cached = this.cache.getCachedTemplate(params.templateType, params as unknown as Record); - if (cached) { - this.logger.debug('Template served from cache', { templateType: params.templateType }); - return cached; - } - - // Generate template using template engine - const template = await this.templateEngine.generateTemplate(params); - - // Cache the result - this.cache.cacheTemplate(params.templateType, params as unknown as Record, template, 300); - - return template; - } - - private async handleListTemplateTypes(): Promise { - return await this.templateEngine.getAvailableTemplates(); - } - - private async handleGetTemplateSchema(args: Record): Promise { - const { templateType } = args as { templateType: string }; - return await this.templateEngine.getTemplateSchema(templateType); - } - - private async handleValidateTemplate(args: Record): Promise { - const { templateType, config } = args as { templateType: string; config: unknown }; - - try { - const isValid = await this.templateEngine.validateTemplate(templateType, config); - return { - valid: isValid, - templateType, - message: 'Template configuration is valid' - }; - } catch (error) { - return { - valid: false, - templateType, - message: error instanceof Error ? error.message : 'Validation failed', - error: error instanceof Error ? error.name : 'Unknown' - }; - } - } - - private async handleGenerateSampleData(args: Record): Promise { - const { dataType, count = 10, seed } = args as { - dataType: string; - count?: number; - seed?: string; - }; - - return await this.templateEngine.generateSampleData(dataType, count, seed); - } - - private async handleCacheStats(): Promise { - const stats = this.cache.getStats(); - return { - cache: stats, - templates: { - engineStats: await this.templateEngine.getStats(), - }, - timestamp: new Date().toISOString(), - }; - } - - private async handleClearCache(args: Record): Promise { - const { pattern } = args as { pattern?: string }; - - if (pattern) { - const deleted = this.cache.invalidatePattern(pattern); - return { - message: `Cleared ${deleted} cache entries matching pattern: ${pattern}`, - deleted, - pattern, - }; - } else { - this.cache.clear(); - return { - message: 'All cache entries cleared', - deleted: 'all', - }; - } - } - - /** - * Create JSON schema for template generation - */ - private createTemplateGenerationSchema(): JSONSchema { - return { - type: 'object', - properties: { - templateType: { - type: 'string', - enum: [ - 'dashboard', 'dataTable', 'productCatalog', 'profileCard', 'timeline', - 'gallery', 'pricing', 'stats', 'calendar', 'wizard', 'chart', 'map', - 'kanban', 'feed', 'form', 'marketplace', 'analytics', 'ecommerce', - 'blog', 'portfolio' - ], - description: 'Type of template to generate' - }, - title: { - type: 'string', - description: 'Title for the template' - }, - description: { - type: 'string', - description: 'Description of the template (optional)' - }, - useCase: { - type: 'string', - description: 'Specific use case or context for the template' - }, - theme: { - type: 'string', - enum: ['light', 'dark', 'system'], - default: 'system', - description: 'Color theme for the template' - }, - primaryColor: { - type: 'string', - pattern: '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$', - description: 'Primary color in hex format' - }, - fullScreen: { - type: 'boolean', - default: false, - description: 'Whether to display in full screen mode' - }, - customData: { - type: 'object', - description: 'Custom data for template customization' - }, - images: { - type: 'array', - items: { - type: 'object', - properties: { - id: { type: 'string' }, - url: { type: 'string' }, - alt: { type: 'string' }, - caption: { type: 'string' }, - category: { type: 'string' } - }, - required: ['id', 'url'] - }, - description: 'Array of images for template' - }, - textContent: { - type: 'object', - additionalProperties: { type: 'string' }, - description: 'Custom text content for different sections' - }, - brandingConfig: { - type: 'object', - properties: { - logoUrl: { type: 'string' }, - brandName: { type: 'string' }, - brandColors: { - type: 'array', - items: { type: 'string' } - }, - fontFamily: { type: 'string' } - }, - description: 'Branding configuration' - } - }, - required: ['templateType', 'title'] - }; - } -} - -interface ToolDefinition { - name: string; - description: string; - inputSchema: JSONSchema; - handler: (args: Record) => Promise; -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/types/mcp.ts b/mcp-ui-server-v2/src/types/mcp.ts deleted file mode 100644 index 15ff442..0000000 --- a/mcp-ui-server-v2/src/types/mcp.ts +++ /dev/null @@ -1,375 +0,0 @@ -/** - * Core MCP Protocol Types - * Based on MCP specification v2024-11-05 - */ - -import { z } from 'zod'; - -// ============================================================================= -// Base Protocol Types -// ============================================================================= - -export const MCPProtocolVersion = '2024-11-05'; - -export interface MCPMessage { - jsonrpc: '2.0'; -} - -export interface MCPRequest extends MCPMessage { - id: string | number; - method: string; - params?: Record; -} - -export interface MCPResponse extends MCPMessage { - id: string | number; - result?: unknown; - error?: MCPError; -} - -export interface MCPNotification extends MCPMessage { - method: string; - params?: Record; -} - -export interface MCPError { - code: number; - message: string; - data?: unknown; -} - -// ============================================================================= -// Capability Types -// ============================================================================= - -export interface ServerCapabilities { - tools?: ToolCapabilities; - resources?: ResourceCapabilities; - prompts?: PromptCapabilities; - logging?: LoggingCapabilities; -} - -export interface ClientCapabilities { - roots?: RootCapabilities; - sampling?: SamplingCapabilities; -} - -export interface ToolCapabilities { - listChanged?: boolean; -} - -export interface ResourceCapabilities { - subscribe?: boolean; - listChanged?: boolean; -} - -export interface PromptCapabilities { - listChanged?: boolean; -} - -export interface LoggingCapabilities { - level?: 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency'; -} - -export interface RootCapabilities { - listChanged?: boolean; -} - -export interface SamplingCapabilities {} - -// ============================================================================= -// Tool Types -// ============================================================================= - -export interface Tool { - name: string; - description?: string; - inputSchema: JSONSchema; -} - -export interface ToolCall { - name: string; - arguments?: Record; -} - -export interface ToolResult { - content: Content[]; - isError?: boolean; -} - -// ============================================================================= -// Resource Types -// ============================================================================= - -export interface Resource { - uri: string; - name: string; - description?: string; - mimeType?: string; -} - -export interface ResourceTemplate { - uriTemplate: string; - name: string; - description?: string; - mimeType?: string; -} - -export interface ResourceContents { - uri: string; - mimeType?: string; - content: Content[]; -} - -// ============================================================================= -// Prompt Types -// ============================================================================= - -export interface Prompt { - name: string; - description?: string; - arguments?: PromptArgument[]; -} - -export interface PromptArgument { - name: string; - description?: string; - required?: boolean; -} - -export interface PromptMessage { - role: 'user' | 'assistant' | 'system'; - content: Content; -} - -export interface GetPromptResult { - description?: string; - messages: PromptMessage[]; -} - -// ============================================================================= -// Content Types -// ============================================================================= - -export type Content = TextContent | ImageContent | EmbeddedResource; - -export interface TextContent { - type: 'text'; - text: string; -} - -export interface ImageContent { - type: 'image'; - data: string; - mimeType: string; -} - -export interface EmbeddedResource { - type: 'resource'; - resource: { - uri: string; - text?: string; - blob?: string; - }; -} - -// ============================================================================= -// JSON Schema Types -// ============================================================================= - -export interface JSONSchema { - type?: string; - properties?: Record; - required?: string[]; - items?: JSONSchema; - additionalProperties?: boolean | JSONSchema; - description?: string; - enum?: unknown[]; - const?: unknown; - default?: unknown; - examples?: unknown[]; - format?: string; - pattern?: string; - minimum?: number; - maximum?: number; - minLength?: number; - maxLength?: number; - minItems?: number; - maxItems?: number; - uniqueItems?: boolean; - multipleOf?: number; - anyOf?: JSONSchema[]; - oneOf?: JSONSchema[]; - allOf?: JSONSchema[]; - not?: JSONSchema; - if?: JSONSchema; - then?: JSONSchema; - else?: JSONSchema; - title?: string; - $schema?: string; - $id?: string; - $ref?: string; - $defs?: Record; -} - -// ============================================================================= -// Template-Specific Types -// ============================================================================= - -export const TemplateTypeSchema = z.enum([ - 'dashboard', - 'dataTable', - 'productCatalog', - 'profileCard', - 'timeline', - 'gallery', - 'pricing', - 'stats', - 'calendar', - 'wizard', - 'chart', - 'map', - 'kanban', - 'feed', - 'form', - 'marketplace', - 'analytics', - 'ecommerce', - 'blog', - 'portfolio' -]); - -export type TemplateType = z.infer; - -export interface TemplateGenerationParams { - templateType: TemplateType; - title: string; - description?: string; - useCase?: string; - theme?: 'light' | 'dark' | 'system'; - primaryColor?: string; - fullScreen?: boolean; - customData?: Record; - images?: ImageData[]; - textContent?: Record; - brandingConfig?: BrandingConfig; - dataSource?: DataSourceConfig; - interactivity?: InteractivityConfig; -} - -export interface ImageData { - id: string; - url: string; - alt?: string; - caption?: string; - category?: string; - metadata?: Record; -} - -export interface BrandingConfig { - logoUrl?: string; - brandName?: string; - brandColors?: string[]; - fontFamily?: string; - favicon?: string; -} - -export interface DataSourceConfig { - type: 'static' | 'api' | 'database' | 'file'; - url?: string; - credentials?: Record; - query?: string; - transformation?: string; - refreshInterval?: number; -} - -export interface InteractivityConfig { - enableSearch?: boolean; - enableFiltering?: boolean; - enableSorting?: boolean; - enablePagination?: boolean; - enableExport?: boolean; - customActions?: ActionDefinition[]; -} - -export interface ActionDefinition { - id: string; - label: string; - type: 'button' | 'link' | 'dropdown'; - trigger: 'onClick' | 'onSubmit' | 'onChange'; - action: { - type: 'navigate' | 'api' | 'event' | 'mcp'; - target?: string; - payload?: Record; - }; -} - -// ============================================================================= -// Server Configuration Types -// ============================================================================= - -export interface ServerConfig { - name: string; - version: string; - description?: string; - author?: string; - license?: string; - capabilities: ServerCapabilities; - transport: TransportConfig; - logging: LoggingConfig; - cache: CacheConfig; - security: SecurityConfig; -} - -export interface TransportConfig { - type: 'stdio' | 'sse' | 'websocket'; - host?: string; - port?: number; - path?: string; - cors?: CORSConfig; -} - -export interface CORSConfig { - origin?: string | string[] | boolean; - methods?: string[]; - allowedHeaders?: string[]; - credentials?: boolean; -} - -export interface LoggingConfig { - level: 'debug' | 'info' | 'warn' | 'error'; - format: 'json' | 'pretty'; - destination?: string; -} - -export interface CacheConfig { - enabled: boolean; - ttl: number; - maxSize: number; - strategy: 'lru' | 'lfu' | 'fifo'; -} - -export interface SecurityConfig { - rateLimit?: RateLimitConfig; - auth?: AuthConfig; - validation?: ValidationConfig; -} - -export interface RateLimitConfig { - enabled: boolean; - maxRequests: number; - windowMs: number; - skipSuccessfulRequests?: boolean; -} - -export interface AuthConfig { - enabled: boolean; - type?: 'apiKey' | 'jwt' | 'oauth2'; - config?: Record; -} - -export interface ValidationConfig { - enabled: boolean; - sanitizeInputs: boolean; - maxPayloadSize: number; -} \ No newline at end of file diff --git a/mcp-ui-server-v2/src/utils/errors.ts b/mcp-ui-server-v2/src/utils/errors.ts deleted file mode 100644 index c21a027..0000000 --- a/mcp-ui-server-v2/src/utils/errors.ts +++ /dev/null @@ -1,348 +0,0 @@ -/** - * Comprehensive error handling for MCP server - * Provides typed errors with proper error codes and context - */ - -import type { MCPError as MCPErrorType } from '../types/mcp.js'; - -/** - * Base MCP Error class - */ -export class MCPError extends Error implements MCPErrorType { - public readonly code: number; - public readonly data?: unknown; - - constructor(code: number, message: string, data?: unknown) { - super(message); - this.name = 'MCPError'; - this.code = code; - this.data = data; - - // Ensure proper prototype chain - Object.setPrototypeOf(this, MCPError.prototype); - } - - toJSON(): MCPErrorType { - return { - code: this.code, - message: this.message, - data: this.data, - }; - } -} - -/** - * Validation errors for invalid input parameters - */ -export class ValidationError extends MCPError { - constructor(message: string, details?: unknown) { - super(-32602, `Validation failed: ${message}`, details); - this.name = 'ValidationError'; - Object.setPrototypeOf(this, ValidationError.prototype); - } -} - -/** - * Protocol-level errors - */ -export class ProtocolError extends MCPError { - constructor(message: string, details?: unknown) { - super(-32601, `Protocol error: ${message}`, details); - this.name = 'ProtocolError'; - Object.setPrototypeOf(this, ProtocolError.prototype); - } -} - -/** - * Tool execution errors - */ -export class ToolExecutionError extends MCPError { - public readonly toolName: string; - - constructor(toolName: string, message: string, details?: unknown) { - super(-32603, `Tool execution failed [${toolName}]: ${message}`, details); - this.name = 'ToolExecutionError'; - this.toolName = toolName; - Object.setPrototypeOf(this, ToolExecutionError.prototype); - } -} - -/** - * Resource access errors - */ -export class ResourceError extends MCPError { - public readonly uri: string; - - constructor(uri: string, message: string, details?: unknown) { - super(-32604, `Resource error [${uri}]: ${message}`, details); - this.name = 'ResourceError'; - this.uri = uri; - Object.setPrototypeOf(this, ResourceError.prototype); - } -} - -/** - * Authentication and authorization errors - */ -export class AuthenticationError extends MCPError { - constructor(message: string, details?: unknown) { - super(-32605, `Authentication failed: ${message}`, details); - this.name = 'AuthenticationError'; - Object.setPrototypeOf(this, AuthenticationError.prototype); - } -} - -export class AuthorizationError extends MCPError { - constructor(resource: string, action: string, details?: unknown) { - super(-32606, `Access denied to ${resource} for action ${action}`, details); - this.name = 'AuthorizationError'; - Object.setPrototypeOf(this, AuthorizationError.prototype); - } -} - -/** - * Rate limiting errors - */ -export class RateLimitError extends MCPError { - public readonly retryAfter?: number; - - constructor(message: string, retryAfter?: number) { - super(-32607, `Rate limit exceeded: ${message}`, { retryAfter }); - this.name = 'RateLimitError'; - if (retryAfter !== undefined) { - this.retryAfter = retryAfter; - } - Object.setPrototypeOf(this, RateLimitError.prototype); - } -} - -/** - * Configuration errors - */ -export class ConfigurationError extends MCPError { - constructor(message: string, details?: unknown) { - super(-32608, `Configuration error: ${message}`, details); - this.name = 'ConfigurationError'; - Object.setPrototypeOf(this, ConfigurationError.prototype); - } -} - -/** - * Data source errors - */ -export class DataSourceError extends MCPError { - public readonly sourceType: string; - - constructor(sourceType: string, message: string, details?: unknown) { - super(-32609, `Data source error [${sourceType}]: ${message}`, details); - this.name = 'DataSourceError'; - this.sourceType = sourceType; - Object.setPrototypeOf(this, DataSourceError.prototype); - } -} - -/** - * Template generation errors - */ -export class TemplateError extends MCPError { - public readonly templateType: string; - - constructor(templateType: string, message: string, details?: unknown) { - super(-32610, `Template error [${templateType}]: ${message}`, details); - this.name = 'TemplateError'; - this.templateType = templateType; - Object.setPrototypeOf(this, TemplateError.prototype); - } -} - -/** - * Cache errors - */ -export class CacheError extends MCPError { - constructor(message: string, details?: unknown) { - super(-32611, `Cache error: ${message}`, details); - this.name = 'CacheError'; - Object.setPrototypeOf(this, CacheError.prototype); - } -} - -/** - * Error utility functions - */ -export class ErrorUtils { - /** - * Check if an error is an MCP error - */ - static isMCPError(error: unknown): error is MCPError { - return error instanceof MCPError; - } - - /** - * Convert any error to an MCP error - */ - static toMCPError(error: unknown): MCPError { - if (ErrorUtils.isMCPError(error)) { - return error; - } - - if (error instanceof Error) { - return new MCPError(-32603, error.message, { - name: error.name, - stack: error.stack, - }); - } - - return new MCPError(-32603, 'Unknown error occurred', { error }); - } - - /** - * Create a validation error from Zod error - */ - static fromZodError(zodError: any): ValidationError { - const issues = zodError.issues || []; - const message = issues - .map((issue: any) => `${issue.path.join('.')}: ${issue.message}`) - .join(', '); - - return new ValidationError(message, { issues }); - } - - /** - * Sanitize error for client response - */ - static sanitizeError(error: MCPError, includeStack: boolean = false): MCPErrorType { - const sanitized: MCPErrorType = { - code: error.code, - message: error.message, - }; - - if (error.data) { - sanitized.data = includeStack ? error.data : this.sanitizeErrorData(error.data); - } - - return sanitized; - } - - private static sanitizeErrorData(data: unknown): unknown { - if (typeof data === 'object' && data !== null) { - const sanitized: Record = {}; - - for (const [key, value] of Object.entries(data)) { - // Remove sensitive information - if (key === 'stack' || key === 'password' || key === 'token' || key === 'secret') { - continue; - } - - sanitized[key] = typeof value === 'object' ? this.sanitizeErrorData(value) : value; - } - - return sanitized; - } - - return data; - } - - /** - * Log error with appropriate level - */ - static getLogLevel(error: MCPError): 'debug' | 'info' | 'warn' | 'error' { - // Client errors (4xx equivalent) - if (error.code >= -32602 && error.code <= -32600) { - return 'warn'; - } - - // Server errors (5xx equivalent) - if (error.code >= -32603) { - return 'error'; - } - - // Authentication/Authorization errors - if (error instanceof AuthenticationError || error instanceof AuthorizationError) { - return 'warn'; - } - - // Rate limiting - if (error instanceof RateLimitError) { - return 'info'; - } - - return 'error'; - } - - /** - * Create error context for logging - */ - static createErrorContext(error: MCPError, additionalContext?: Record): Record { - const context: Record = { - errorType: error.name, - errorCode: error.code, - errorMessage: error.message, - ...additionalContext, - }; - - // Add specific error properties - if (error instanceof ToolExecutionError) { - context.toolName = error.toolName; - } else if (error instanceof ResourceError) { - context.resourceUri = error.uri; - } else if (error instanceof TemplateError) { - context.templateType = error.templateType; - } else if (error instanceof DataSourceError) { - context.dataSourceType = error.sourceType; - } else if (error instanceof RateLimitError && error.retryAfter) { - context.retryAfter = error.retryAfter; - } - - return context; - } -} - -/** - * Error handler decorator for async functions - */ -export function handleErrors Promise>( - target: any, - propertyKey: string, - descriptor: TypedPropertyDescriptor -): TypedPropertyDescriptor { - const originalMethod = descriptor.value; - - if (!originalMethod) { - return descriptor; - } - - descriptor.value = (async function(this: any, ...args: any[]) { - try { - return await originalMethod.apply(this, args); - } catch (error) { - throw ErrorUtils.toMCPError(error); - } - }) as T; - - return descriptor; -} - -/** - * Async error boundary for promise chains - */ -export class AsyncErrorBoundary { - static async execute( - operation: () => Promise, - errorHandler?: (error: MCPError) => MCPError | void - ): Promise { - try { - return await operation(); - } catch (error) { - const mcpError = ErrorUtils.toMCPError(error); - - if (errorHandler) { - const handledError = errorHandler(mcpError); - if (handledError) { - throw handledError; - } - } - - throw mcpError; - } - } -} \ No newline at end of file diff --git a/scripts/start-mcp-server.js b/scripts/start-mcp-server.js index 97975bb..46e42cf 100644 --- a/scripts/start-mcp-server.js +++ b/scripts/start-mcp-server.js @@ -12,7 +12,7 @@ const path = require('path'); const fs = require('fs'); // Configuration -const MCP_SERVER_PATH = path.join(__dirname, '..', 'mcp-ui-server-v2', 'dist', 'index.js'); +const MCP_SERVER_PATH = path.join(__dirname, '..', 'mcp-ui-server-v2', 'dist', 'simple-server.js'); const PID_FILE = path.join(__dirname, '..', '.mcp-server.pid'); const LOG_FILE = path.join(__dirname, '..', '.mcp-server.log'); @@ -78,13 +78,10 @@ function startServer() { log('Starting MCP server...', 'blue'); - // Prepare log file - const logStream = fs.createWriteStream(LOG_FILE, { flags: 'a' }); - - // Start the MCP server process + // Start the MCP server process const mcpServer = spawn('node', [MCP_SERVER_PATH], { detached: true, - stdio: ['ignore', logStream, logStream], + stdio: ['ignore', 'pipe', 'pipe'], env: { ...process.env, NODE_ENV: 'production', @@ -92,6 +89,19 @@ function startServer() { } }); + // Handle output logging + if (mcpServer.stdout) { + mcpServer.stdout.on('data', (data) => { + fs.appendFileSync(LOG_FILE, `[STDOUT] ${data}`); + }); + } + + if (mcpServer.stderr) { + mcpServer.stderr.on('data', (data) => { + fs.appendFileSync(LOG_FILE, `[STDERR] ${data}`); + }); + } + // Handle server startup mcpServer.on('spawn', () => { log(`MCP server started successfully (PID: ${mcpServer.pid})`, 'green');