UNPKG

backend-mcp

Version:

Generador automático de backends con Node.js, Express, Prisma y módulos configurables. Servidor MCP compatible con npx para agentes IA. Soporta PostgreSQL, MySQL, MongoDB y SQLite.

1,265 lines (1,163 loc) 40.6 kB
#!/usr/bin/env node /** * Backend MCP Advanced Tools * Sistema completo de herramientas MCP para control granular de la IA */ const fs = require('fs'); const path = require('path'); const yaml = require('js-yaml'); const { spawn } = require('child_process'); /** * Herramientas MCP Avanzadas para Backend Generator * Cada herramienta permite control granular sobre aspectos específicos */ const MCP_TOOLS = { // ==================== CONFIGURACIÓN DE PROYECTO ==================== configure_project: { name: 'configure_project', description: 'Configura los parámetros generales del proyecto backend', inputSchema: { type: 'object', properties: { projectName: { type: 'string', description: 'Nombre del proyecto' }, description: { type: 'string', description: 'Descripción del proyecto' }, version: { type: 'string', default: '1.0.0' }, author: { type: 'string' }, license: { type: 'string', default: 'MIT' }, nodeVersion: { type: 'string', default: '>=18.0.0' }, packageManager: { type: 'string', enum: ['npm', 'yarn', 'pnpm'], default: 'npm' } }, required: ['projectName', 'description'] }, examples: [ { projectName: 'ecommerce-api', description: 'API completa para e-commerce', author: 'Mi Empresa', license: 'MIT' } ] }, // ==================== GESTIÓN DE MÓDULOS ==================== add_module: { name: 'add_module', description: 'Añade un módulo específico al proyecto', inputSchema: { type: 'object', properties: { moduleName: { type: 'string', enum: ['auth', 'database', 'crud', 'email', 'logging', 'websockets', 'cache', 'monitoring', 'validation', 'notifications', 'docker', 'ci', 'testing'] }, config: { type: 'object', description: 'Configuración específica del módulo' }, priority: { type: 'number', default: 1, description: 'Prioridad de ejecución' } }, required: ['moduleName'] }, examples: [ { moduleName: 'auth', config: { jwtSecret: 'auto-generate', tokenExpiry: '24h', roles: ['admin', 'user'], enableRefreshTokens: true } } ] }, // ==================== BASE DE DATOS ==================== configure_database: { name: 'configure_database', description: 'Configura dinámicamente la conexión a la base de datos. Permite configurar la DATABASE_URL en tiempo de ejecución sin reiniciar el servidor MCP.', inputSchema: { type: 'object', properties: { provider: { type: 'string', enum: ['postgresql', 'mysql', 'sqlite', 'mongodb'] }, url: { type: 'string', description: 'URL completa de conexión a la base de datos (ej: postgresql://username:password@localhost:5432/database_name?schema=public)' }, host: { type: 'string', description: 'Host de la base de datos (alternativa a URL)' }, port: { type: 'number', description: 'Puerto de la base de datos' }, database: { type: 'string', description: 'Nombre de la base de datos' }, username: { type: 'string', description: 'Usuario de la base de datos' }, password: { type: 'string', description: 'Contraseña de la base de datos' }, schema: { type: 'string', description: 'Esquema de la base de datos (para PostgreSQL)', default: 'public' }, ssl: { type: 'boolean', description: 'Habilitar SSL', default: false }, enableMigrations: { type: 'boolean', default: true }, enableSeeding: { type: 'boolean', default: true }, poolSize: { type: 'number', default: 10 }, connectionTimeout: { type: 'number', default: 30000, description: 'Timeout de conexión en ms' }, queryTimeout: { type: 'number', default: 60000, description: 'Timeout de consulta en ms' }, testConnection: { type: 'boolean', default: true, description: 'Probar la conexión después de configurar' } }, required: [], oneOf: [ { required: ['url'] }, { required: ['provider', 'host', 'port', 'database', 'username', 'password'] }, { required: ['provider'] } ] }, examples: [ { provider: 'postgresql', url: 'postgresql://username:password@localhost:5432/database_name?schema=public', enableMigrations: true, enableSeeding: true, poolSize: 10 }, { provider: 'postgresql', host: 'localhost', port: 5432, database: 'ecommerce_db', username: 'postgres', password: 'mypassword', schema: 'public', ssl: false }, { provider: 'mysql', url: 'mysql://user:password@localhost:3306/mydb', poolSize: 15 }, { provider: 'sqlite', url: 'file:./dev.db', enableMigrations: true } ] }, define_table: { name: 'define_table', description: 'Define una tabla/modelo en la base de datos', inputSchema: { type: 'object', properties: { tableName: { type: 'string' }, fields: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, type: { type: 'string', enum: ['String', 'Int', 'Float', 'Boolean', 'DateTime', 'Json'] }, required: { type: 'boolean', default: false }, unique: { type: 'boolean', default: false }, defaultValue: { type: 'string' } }, required: ['name', 'type'] } }, relations: { type: 'array', items: { type: 'object', properties: { type: { type: 'string', enum: ['oneToOne', 'oneToMany', 'manyToMany'] }, relatedTable: { type: 'string' }, foreignKey: { type: 'string' } } } } }, required: ['tableName', 'fields'] }, examples: [ { tableName: 'User', fields: [ { name: 'id', type: 'Int', required: true, unique: true }, { name: 'email', type: 'String', required: true, unique: true }, { name: 'password', type: 'String', required: true }, { name: 'role', type: 'String', defaultValue: 'user' }, { name: 'createdAt', type: 'DateTime', defaultValue: 'now()' } ] } ] }, // ==================== AUTENTICACIÓN ==================== setup_auth: { name: 'setup_auth', description: 'Configura el sistema de autenticación', inputSchema: { type: 'object', properties: { strategy: { type: 'string', enum: ['jwt', 'session', 'oauth'], default: 'jwt' }, providers: { type: 'array', items: { type: 'string', enum: ['local', 'google', 'github', 'facebook'] } }, jwtConfig: { type: 'object', properties: { secret: { type: 'string' }, expiresIn: { type: 'string', default: '24h' }, algorithm: { type: 'string', default: 'HS256' } } }, roles: { type: 'array', items: { type: 'string' } }, permissions: { type: 'array', items: { type: 'object', properties: { resource: { type: 'string' }, actions: { type: 'array', items: { type: 'string' } }, roles: { type: 'array', items: { type: 'string' } } } } } }, required: ['strategy'] } }, // ==================== API ENDPOINTS ==================== create_endpoint: { name: 'create_endpoint', description: 'Crea un endpoint personalizado de API', inputSchema: { type: 'object', properties: { path: { type: 'string' }, method: { type: 'string', enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'] }, controller: { type: 'string' }, middleware: { type: 'array', items: { type: 'string' } }, validation: { type: 'object', properties: { body: { type: 'object' }, params: { type: 'object' }, query: { type: 'object' } } }, response: { type: 'object', properties: { success: { type: 'object' }, error: { type: 'object' } } }, requiresAuth: { type: 'boolean', default: false }, roles: { type: 'array', items: { type: 'string' } } }, required: ['path', 'method', 'controller'] } }, // ==================== EMAIL Y NOTIFICACIONES ==================== setup_email: { name: 'setup_email', description: 'Configura el sistema de emails', inputSchema: { type: 'object', properties: { provider: { type: 'string', enum: ['smtp', 'sendgrid', 'mailgun', 'ses'] }, config: { type: 'object', properties: { host: { type: 'string' }, port: { type: 'number' }, secure: { type: 'boolean' }, auth: { type: 'object', properties: { user: { type: 'string' }, pass: { type: 'string' } } } } }, templates: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, subject: { type: 'string' }, htmlTemplate: { type: 'string' }, textTemplate: { type: 'string' } } } } }, required: ['provider'] } }, // ==================== WEBSOCKETS ==================== setup_websockets: { name: 'setup_websockets', description: 'Configura websockets para tiempo real', inputSchema: { type: 'object', properties: { enabled: { type: 'boolean', default: true }, port: { type: 'number', default: 3001 }, cors: { type: 'object', properties: { origin: { type: 'string' }, credentials: { type: 'boolean' } } }, events: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, handler: { type: 'string' }, requiresAuth: { type: 'boolean', default: false } } } }, rooms: { type: 'array', items: { type: 'string' } } } } }, // ==================== CACHE ==================== setup_cache: { name: 'setup_cache', description: 'Configura el sistema de cache', inputSchema: { type: 'object', properties: { provider: { type: 'string', enum: ['redis', 'memory', 'memcached'] }, config: { type: 'object', properties: { host: { type: 'string' }, port: { type: 'number' }, password: { type: 'string' }, db: { type: 'number', default: 0 } } }, defaultTTL: { type: 'number', default: 3600 }, strategies: { type: 'array', items: { type: 'object', properties: { key: { type: 'string' }, ttl: { type: 'number' }, invalidateOn: { type: 'array', items: { type: 'string' } } } } } }, required: ['provider'] } }, // ==================== MONITOREO ==================== setup_monitoring: { name: 'setup_monitoring', description: 'Configura monitoreo y métricas', inputSchema: { type: 'object', properties: { enabled: { type: 'boolean', default: true }, metrics: { type: 'array', items: { type: 'string', enum: ['requests', 'errors', 'performance', 'database', 'memory', 'cpu'] } }, alerts: { type: 'array', items: { type: 'object', properties: { metric: { type: 'string' }, threshold: { type: 'number' }, action: { type: 'string' } } } }, dashboards: { type: 'boolean', default: true } } } }, // ==================== DOCKER ==================== setup_docker: { name: 'setup_docker', description: 'Configura contenedores Docker', inputSchema: { type: 'object', properties: { enabled: { type: 'boolean', default: true }, baseImage: { type: 'string', default: 'node:18-alpine' }, services: { type: 'array', items: { type: 'object', properties: { name: { type: 'string' }, image: { type: 'string' }, ports: { type: 'array', items: { type: 'string' } }, environment: { type: 'object' } } } }, volumes: { type: 'array', items: { type: 'string' } }, networks: { type: 'array', items: { type: 'string' } } } } }, // ==================== TESTING ==================== setup_testing: { name: 'setup_testing', description: 'Configura el framework de testing', inputSchema: { type: 'object', properties: { framework: { type: 'string', enum: ['jest', 'mocha', 'vitest'], default: 'jest' }, coverage: { type: 'boolean', default: true }, testTypes: { type: 'array', items: { type: 'string', enum: ['unit', 'integration', 'e2e'] } }, mocking: { type: 'boolean', default: true }, fixtures: { type: 'boolean', default: true } } } }, // ==================== VALIDACIÓN ==================== validate_config: { name: 'validate_config', description: 'Valida la configuración completa del proyecto', inputSchema: { type: 'object', properties: { strict: { type: 'boolean', default: false }, checkDependencies: { type: 'boolean', default: true }, validateSchemas: { type: 'boolean', default: true } } } }, // ==================== GENERACIÓN ==================== generate_project: { name: 'generate_project', description: 'Genera el proyecto completo con toda la configuración', inputSchema: { type: 'object', properties: { outputPath: { type: 'string' }, overwrite: { type: 'boolean', default: false }, installDependencies: { type: 'boolean', default: true }, runMigrations: { type: 'boolean', default: true }, generateDocs: { type: 'boolean', default: true }, generateContract: { type: 'boolean', default: true, description: 'Generar contrato frontend automáticamente' } } } }, // ==================== CONTRATO FRONTEND ==================== generate_contract: { name: 'generate_contract', description: 'Genera el contrato JSON para sincronización frontend-backend automática', inputSchema: { type: 'object', properties: { outputPath: { type: 'string', description: 'Ruta donde generar el contrato (por defecto: ./frontend-contract.json)' }, includeEntities: { type: 'boolean', default: true, description: 'Incluir análisis de entidades CRUD' }, includeAuth: { type: 'boolean', default: true, description: 'Incluir configuración de autenticación' }, includeAPI: { type: 'boolean', default: true, description: 'Incluir endpoints y configuración de API' }, includeUI: { type: 'boolean', default: true, description: 'Incluir configuración de UI y componentes' }, includeValidations: { type: 'boolean', default: true, description: 'Incluir reglas de validación' }, targetFramework: { type: 'string', enum: ['react', 'vue', 'angular', 'all'], default: 'all', description: 'Framework objetivo para optimizar el contrato' }, generateTypes: { type: 'boolean', default: true, description: 'Generar definiciones TypeScript' }, verbose: { type: 'boolean', default: false, description: 'Mostrar información detallada durante la generación' } } }, examples: [ { outputPath: './frontend-contract.json', targetFramework: 'react', generateTypes: true }, { outputPath: './contracts/api-contract.json', includeUI: false, targetFramework: 'vue' } ] } }; /** * Configuración global del proyecto */ let projectConfig = { project: {}, modules: [], database: {}, auth: {}, endpoints: [], email: {}, websockets: {}, cache: {}, monitoring: {}, docker: {}, testing: {} }; /** * Archivo de configuración persistente */ const CONFIG_FILE = path.join(__dirname, '.mcp-project-config.json'); /** * Carga la configuración desde archivo */ function loadProjectConfig() { try { if (fs.existsSync(CONFIG_FILE)) { const configData = fs.readFileSync(CONFIG_FILE, 'utf8'); const loadedConfig = JSON.parse(configData); projectConfig = { ...projectConfig, ...loadedConfig }; console.error(`📋 Configuración cargada desde: ${CONFIG_FILE}`); } } catch (error) { console.error(`⚠️ Error al cargar configuración: ${error.message}`); } } /** * Guarda la configuración en archivo */ function saveProjectConfig() { try { console.error(`🔄 Intentando guardar configuración en: ${CONFIG_FILE}`); console.error(`📊 Datos a guardar:`, JSON.stringify(projectConfig, null, 2)); fs.writeFileSync(CONFIG_FILE, JSON.stringify(projectConfig, null, 2)); console.error(`💾 Configuración guardada exitosamente en: ${CONFIG_FILE}`); // Verificar que el archivo se creó if (fs.existsSync(CONFIG_FILE)) { console.error(`✅ Archivo verificado: ${CONFIG_FILE}`); } else { console.error(`❌ Error: El archivo no se creó: ${CONFIG_FILE}`); } } catch (error) { console.error(`⚠️ Error al guardar configuración: ${error.message}`); console.error(`📍 Stack trace:`, error.stack); } } // Cargar configuración al inicializar loadProjectConfig(); /** * Ejecuta una herramienta MCP específica */ async function executeTool(toolName, params) { const tool = MCP_TOOLS[toolName]; if (!tool) { throw new Error(`Herramienta '${toolName}' no encontrada`); } // Validar parámetros if (!validateParams(params, tool.inputSchema)) { throw new Error(`Parámetros inválidos para '${toolName}'`); } // Ejecutar la herramienta switch (toolName) { case 'configure_project': return configureProject(params); case 'add_module': return addModule(params); case 'configure_database': return configureDatabase(params); case 'define_table': return defineTable(params); case 'setup_auth': return setupAuth(params); case 'create_endpoint': return createEndpoint(params); case 'setup_email': return setupEmail(params); case 'setup_websockets': return setupWebsockets(params); case 'setup_cache': return setupCache(params); case 'setup_monitoring': return setupMonitoring(params); case 'setup_docker': return setupDocker(params); case 'setup_testing': return setupTesting(params); case 'validate_config': return validateConfig(params); case 'generate_project': return await generateProject(params); case 'generate_contract': return await generateContract(params); default: throw new Error(`Implementación de '${toolName}' no encontrada`); } } /** * Función auxiliar para probar conexión a base de datos */ function testDatabaseConnection(databaseUrl, provider) { try { // Validación básica de URL if (!databaseUrl || typeof databaseUrl !== 'string') { return { success: false, message: 'URL de base de datos inválida' }; } // Validaciones específicas por proveedor switch (provider) { case 'postgresql': if (!databaseUrl.match(/^postgres(ql)?:\/\/.+/)) { return { success: false, message: 'URL de PostgreSQL inválida' }; } break; case 'mysql': if (!databaseUrl.match(/^mysql:\/\/.+/)) { return { success: false, message: 'URL de MySQL inválida' }; } break; case 'mongodb': if (!databaseUrl.match(/^mongodb(\+srv)?:\/\/.+/)) { return { success: false, message: 'URL de MongoDB inválida' }; } break; case 'sqlite': if (!databaseUrl.match(/^file:.+/) && !databaseUrl.includes('.db')) { return { success: false, message: 'URL de SQLite inválida' }; } break; default: return { success: false, message: `Proveedor '${provider}' no soportado` }; } // Si llegamos aquí, la URL parece válida return { success: true, message: `URL de ${provider} válida`, note: 'Validación de formato completada. Para una prueba de conexión real, se requiere el cliente de base de datos correspondiente.' }; } catch (error) { return { success: false, message: `Error al validar conexión: ${error.message}` }; } } /** * Implementaciones de las herramientas */ function configureProject(params) { projectConfig.project = { ...projectConfig.project, ...params }; // Intentar guardar configuración try { saveProjectConfig(); const fileExists = fs.existsSync(CONFIG_FILE); return { success: true, message: 'Proyecto configurado', config: projectConfig.project, debug: { configFilePath: CONFIG_FILE, fileCreated: fileExists, projectConfigKeys: Object.keys(projectConfig) } }; } catch (error) { return { success: true, message: 'Proyecto configurado (error al guardar)', config: projectConfig.project, saveError: error.message }; } } function addModule(params) { const existingModule = projectConfig.modules.find(m => m.name === params.moduleName); if (existingModule) { existingModule.config = { ...existingModule.config, ...params.config }; } else { projectConfig.modules.push({ name: params.moduleName, config: params.config || {}, priority: params.priority || 1 }); } saveProjectConfig(); return { success: true, message: `Módulo '${params.moduleName}' añadido`, modules: projectConfig.modules }; } function configureDatabase(params) { try { // Si no se proporcionan parámetros, mostrar configuración actual if (!params || Object.keys(params).length === 0) { const currentConfig = { databaseUrl: process.env.DATABASE_URL || 'No configurada', provider: projectConfig.database?.provider || 'No detectado', status: process.env.DATABASE_URL ? 'Configurada' : 'No configurada' }; return { success: true, message: 'Estado actual de la base de datos', config: currentConfig }; } let databaseUrl = ''; let provider = params.provider; // Construir URL de conexión if (params.url) { databaseUrl = params.url; // Detectar proveedor desde URL si no se especifica if (!provider) { if (databaseUrl.startsWith('postgresql://') || databaseUrl.startsWith('postgres://')) { provider = 'postgresql'; } else if (databaseUrl.startsWith('mysql://')) { provider = 'mysql'; } else if (databaseUrl.startsWith('mongodb://') || databaseUrl.startsWith('mongodb+srv://')) { provider = 'mongodb'; } else if (databaseUrl.startsWith('file:') || databaseUrl.includes('.db')) { provider = 'sqlite'; } } } else if (params.host && params.port && params.database && params.username && params.password) { // Construir URL desde componentes individuales const protocol = provider === 'postgresql' ? 'postgresql' : provider === 'mysql' ? 'mysql' : provider === 'mongodb' ? 'mongodb' : 'sqlite'; if (provider === 'sqlite') { databaseUrl = `file:${params.database}`; } else { const auth = `${params.username}:${params.password}`; const hostPort = `${params.host}:${params.port}`; const dbName = params.database; const schema = params.schema ? `?schema=${params.schema}` : ''; databaseUrl = `${protocol}://${auth}@${hostPort}/${dbName}${schema}`; } } else if (provider === 'sqlite' && !params.url) { // SQLite por defecto databaseUrl = 'file:./database.db'; } // Validar URL de base de datos if (!databaseUrl) { return { success: false, message: 'Error: Debes proporcionar una URL o los parámetros de conexión (host, port, database, username, password)', examples: { postgresql: 'postgresql://user:password@localhost:5432/database', mysql: 'mysql://user:password@localhost:3306/database', mongodb: 'mongodb://user:password@localhost:27017/database', sqlite: 'file:./database.db' } }; } // Configurar DATABASE_URL en el entorno process.env.DATABASE_URL = databaseUrl; // Actualizar configuración del proyecto projectConfig.database = { ...projectConfig.database, provider: provider, url: databaseUrl, configuredDynamically: true, configuredAt: new Date().toISOString(), ...params }; // Probar conexión si se solicita let connectionTest = { success: true, message: 'Conexión no probada' }; if (params.testConnection !== false) { connectionTest = testDatabaseConnection(databaseUrl, provider); } const maskedUrl = databaseUrl.replace(/:\/\/[^:]+:[^@]+@/, '://***:***@'); saveProjectConfig(); return { success: true, message: 'Base de datos configurada dinámicamente', config: { provider: provider, url: maskedUrl, status: 'Configurada', configuredAt: projectConfig.database.configuredAt }, connectionTest: connectionTest }; } catch (error) { return { success: false, message: `Error al configurar base de datos: ${error.message}`, error: error.toString() }; } // Código de retrocompatibilidad para configuración desde agente if (projectConfig.database && projectConfig.database.configuredFromAgent) { console.log('⚠️ Base de datos ya configurada desde el agente, preservando configuración existente'); const allowedUpdates = ['schema', 'ssl', 'enableMigrations', 'enableSeeding', 'poolSize', 'connectionTimeout', 'queryTimeout']; const filteredParams = {}; for (const [key, value] of Object.entries(params)) { if (allowedUpdates.includes(key)) { filteredParams[key] = value; } } projectConfig.database = { ...projectConfig.database, ...filteredParams }; return { success: true, message: 'Base de datos actualizada (configuración del agente preservada)', config: projectConfig.database, note: 'URL y proveedor configurados desde el agente no fueron modificados' }; } // Configuración normal si no viene del agente projectConfig.database = { ...projectConfig.database, ...params }; return { success: true, message: 'Base de datos configurada', config: projectConfig.database }; } function defineTable(params) { if (!projectConfig.database.tables) { projectConfig.database.tables = []; } projectConfig.database.tables.push(params); saveProjectConfig(); return { success: true, message: `Tabla '${params.tableName}' definida`, tables: projectConfig.database.tables }; } function setupAuth(params) { projectConfig.auth = { ...projectConfig.auth, ...params }; return { success: true, message: 'Autenticación configurada', config: projectConfig.auth }; } function createEndpoint(params) { projectConfig.endpoints.push(params); return { success: true, message: `Endpoint '${params.method} ${params.path}' creado`, endpoints: projectConfig.endpoints }; } function setupEmail(params) { projectConfig.email = { ...projectConfig.email, ...params }; return { success: true, message: 'Email configurado', config: projectConfig.email }; } function setupWebsockets(params) { projectConfig.websockets = { ...projectConfig.websockets, ...params }; return { success: true, message: 'Websockets configurados', config: projectConfig.websockets }; } function setupCache(params) { projectConfig.cache = { ...projectConfig.cache, ...params }; return { success: true, message: 'Cache configurado', config: projectConfig.cache }; } function setupMonitoring(params) { projectConfig.monitoring = { ...projectConfig.monitoring, ...params }; return { success: true, message: 'Monitoreo configurado', config: projectConfig.monitoring }; } function setupDocker(params) { projectConfig.docker = { ...projectConfig.docker, ...params }; return { success: true, message: 'Docker configurado', config: projectConfig.docker }; } function setupTesting(params) { projectConfig.testing = { ...projectConfig.testing, ...params }; return { success: true, message: 'Testing configurado', config: projectConfig.testing }; } function validateConfig(params) { const errors = []; const warnings = []; // Validaciones básicas if (!projectConfig.project.projectName) { errors.push('Nombre del proyecto requerido'); } if (projectConfig.modules.length === 0) { warnings.push('No se han configurado módulos'); } return { success: errors.length === 0, errors, warnings, config: projectConfig }; } /** * Genera el contrato frontend-backend automáticamente */ async function generateContract(params) { try { // Importar el generador de contratos const ContractGenerator = require('./scripts/generate-contract'); // Configurar parámetros por defecto const defaultOutputPath = path.join(process.cwd(), 'frontend-contract.json'); const outputPath = params.outputPath || defaultOutputPath; if (params.verbose) { console.log(`📋 Generando contrato frontend-backend...`); console.log(`📂 Ruta de salida: ${outputPath}`); console.log(`🎯 Framework objetivo: ${params.targetFramework || 'all'}`); } // Crear instancia del generador const generator = new ContractGenerator(process.cwd()); // Generar el contrato const contract = await generator.generateContract(); // Guardar el contrato fs.writeFileSync(outputPath, JSON.stringify(contract, null, 2)); if (params.verbose) { console.log(`✅ Contrato generado exitosamente`); console.log(`📊 Entidades analizadas: ${contract.entities?.length || 0}`); console.log(`🔐 Módulos de auth: ${contract.modules?.auth?.enabled ? 'Sí' : 'No'}`); console.log(`🌐 Endpoints API: ${Object.keys(contract.api?.endpoints || {}).length}`); } return { success: true, message: `Contrato frontend generado exitosamente en: ${outputPath}`, outputPath: outputPath, contract: { version: contract.contract?.version, entitiesCount: contract.entities?.length || 0, modulesEnabled: Object.keys(contract.modules || {}).filter(m => contract.modules[m]?.enabled).length, endpointsCount: Object.keys(contract.api?.endpoints || {}).length, targetFramework: params.targetFramework || 'all', checksums: contract.contract?.compatibility?.checksums }, config: projectConfig }; } catch (error) { console.error('❌ Error en generateContract:', error); return { success: false, message: `Error generando contrato: ${error.message}`, error: error.toString(), outputPath: params.outputPath }; } } async function generateProject(params) { try { // Importar el orchestrator const { BackendOrchestrator } = require('./orchestrator'); const orchestrator = new BackendOrchestrator(); // Determinar la ruta de salida // Si no se especifica outputPath, usar el directorio de trabajo del usuario que ejecutó npx const userWorkingDirectory = projectConfig.userWorkingDirectory || process.cwd(); const baseOutputPath = params.outputPath || userWorkingDirectory; // Validar que el proyecto esté configurado if (!projectConfig.project.projectName) { return { success: false, message: 'Error: Debes configurar el proyecto primero usando configure_project' }; } // Crear directorio específico para el proyecto const projectDirectoryName = projectConfig.project.projectName.toLowerCase().replace(/[^a-z0-9-]/g, '-'); const outputPath = path.join(baseOutputPath, projectDirectoryName, 'backend'); // Crear el directorio del proyecto si no existe if (!fs.existsSync(outputPath)) { fs.mkdirSync(outputPath, { recursive: true }); console.log(`📁 Directorio del proyecto creado: ${outputPath}`); } else { console.log(`📁 Directorio del proyecto ya existe: ${outputPath}`); } console.log(`📁 Directorio de trabajo del usuario: ${userWorkingDirectory}`); console.log(`📂 Directorio de salida del proyecto: ${outputPath}`); // Preparar opciones para el orchestrator const orchestratorOptions = { input: projectConfig.project.description || 'Generated backend project', outputPath: outputPath, projectName: projectConfig.project.projectName, modules: projectConfig.modules.map(m => m.name), database: projectConfig.database, auth: projectConfig.auth, endpoints: projectConfig.endpoints, installDependencies: params.installDependencies !== false, runMigrations: params.runMigrations !== false }; console.log(`🚀 Generando proyecto en: ${outputPath}`); console.log(`📦 Módulos configurados: ${orchestratorOptions.modules.join(', ')}`); // Ejecutar la generación const result = await orchestrator.generate(orchestratorOptions); if (result.success) { console.log(`✅ Proyecto generado exitosamente en: ${outputPath}`); console.log(`📁 Archivos generados: ${result.filesGenerated}`); console.log(`⏱️ Tiempo de ejecución: ${result.executionTime}ms`); // Generar contrato automáticamente si está habilitado let contractResult = null; if (params.generateContract !== false) { console.log(`📋 Generando contrato frontend automáticamente...`); try { // Cambiar al directorio del proyecto generado temporalmente const originalCwd = process.cwd(); process.chdir(outputPath); contractResult = await generateContract({ outputPath: path.join(outputPath, 'frontend-contract.json'), verbose: true }); // Restaurar el directorio original process.chdir(originalCwd); if (contractResult.success) { console.log(`✅ Contrato frontend generado: ${contractResult.outputPath}`); } else { console.log(`⚠️ Error generando contrato: ${contractResult.message}`); } } catch (contractError) { // Restaurar el directorio original en caso de error process.chdir(originalCwd); console.log(`⚠️ Error generando contrato: ${contractError.message}`); contractResult = { success: false, message: contractError.message }; } } return { success: true, message: `Proyecto '${projectConfig.project.projectName}' generado exitosamente`, outputPath: outputPath, filesGenerated: result.filesGenerated, modulesExecuted: result.modulesExecuted, executionTime: result.executionTime, config: projectConfig, contract: contractResult }; } else { return { success: false, message: `Error generando proyecto: ${result.error}`, outputPath: outputPath, error: result.error }; } } catch (error) { console.error('❌ Error en generateProject:', error); return { success: false, message: `Error interno: ${error.message}`, error: error.toString() }; } } /** * Validador de parámetros */ function validateParams(params, schema) { // Implementación básica de validación // En producción usaríamos una librería como Joi o Ajv return true; } /** * Función para validar entrada de herramientas */ function validateToolInput(toolName, input) { const tool = MCP_TOOLS[toolName]; if (!tool) { return { valid: false, errors: [`Herramienta '${toolName}' no encontrada`] }; } const schema = tool.inputSchema; const errors = []; // Validación básica de propiedades requeridas if (schema.required) { for (const requiredField of schema.required) { if (!(requiredField in input)) { errors.push(`Campo requerido '${requiredField}' faltante`); } } } // Validación de tipos básicos if (schema.properties) { for (const [fieldName, fieldSchema] of Object.entries(schema.properties)) { if (fieldName in input) { const value = input[fieldName]; const expectedType = fieldSchema.type; if (expectedType === 'string' && typeof value !== 'string') { errors.push(`Campo '${fieldName}' debe ser string`); } if (expectedType === 'number' && typeof value !== 'number') { errors.push(`Campo '${fieldName}' debe ser number`); } if (expectedType === 'boolean' && typeof value !== 'boolean') { errors.push(`Campo '${fieldName}' debe ser boolean`); } if (expectedType === 'array' && !Array.isArray(value)) { errors.push(`Campo '${fieldName}' debe ser array`); } if (expectedType === 'object' && (typeof value !== 'object' || Array.isArray(value))) { errors.push(`Campo '${fieldName}' debe ser object`); } } } } return { valid: errors.length === 0, errors }; } /** * Función para ejecutar cadena de herramientas */ function executeToolChain(config) { const results = []; for (const step of config.steps) { const validation = validateToolInput(step.tool, step.params); if (!validation.valid) { throw new Error(`Validación falló para ${step.tool}: ${validation.errors.join(', ')}`); } // Simular ejecución de herramienta results.push({ tool: step.tool, status: 'success', params: step.params }); } return results; } /** * Función para generar proyecto desde configuración */ function generateProjectFromConfig(configPath) { const config = JSON.parse(fs.readFileSync(configPath, 'utf8')); return executeToolChain(config); } /** * Exportar herramientas y funciones */ module.exports = { MCP_TOOLS, mcpTools: MCP_TOOLS, executeTool, projectConfig, validateConfig, generateProject, validateToolInput, executeToolChain, generateProjectFromConfig }; // CLI para testing if (require.main === module) { (async () => { const args = process.argv.slice(2); if (args.length < 2) { console.log('Uso: node mcp-tools.js <tool> <params-json>'); console.log('Herramientas disponibles:', Object.keys(MCP_TOOLS).join(', ')); process.exit(1); } const toolName = args[0]; const params = JSON.parse(args[1]); try { const result = await executeTool(toolName, params); console.log(JSON.stringify(result, null, 2)); } catch (error) { console.error('Error:', error.message); process.exit(1); } })(); }