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
JavaScript
/**
* 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);
}
})();
}