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.
494 lines (407 loc) β’ 14.1 kB
JavaScript
/**
* MCP Backend Project Initialization Script
* Automates the setup of a new backend project with selected modules
*/
const fs = require('fs');
const path = require('path');
const { execSync } = require('child_process');
const readline = require('readline');
class ProjectInitializer {
constructor() {
this.projectName = '';
this.projectPath = '';
this.selectedModules = [];
this.databaseType = 'postgres';
this.features = {
docker: true,
ci: true,
monitoring: true,
testing: true
};
}
async init() {
console.log('π MCP Backend Project Initializer');
console.log('===================================\n');
try {
await this.promptProjectDetails();
await this.selectModules();
await this.selectFeatures();
await this.createProject();
await this.installDependencies();
await this.generateDocumentation();
console.log('\nπ Project initialized successfully!');
console.log(`π Project location: ${this.projectPath}`);
console.log('\nπ Next steps:');
console.log('1. cd ' + this.projectName);
console.log('2. npm run dev');
console.log('3. Open http://localhost:3000');
} catch (error) {
console.error('β Error initializing project:', error.message);
process.exit(1);
}
}
async promptProjectDetails() {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
this.projectName = await this.question(rl, 'π Project name: ');
this.projectPath = path.join(process.cwd(), this.projectName);
const dbChoice = await this.question(rl, 'ποΈ Database type (postgres/mysql) [postgres]: ');
this.databaseType = dbChoice.toLowerCase() || 'postgres';
rl.close();
}
async selectModules() {
console.log('\nπ¦ Available modules:');
const availableModules = [
{ name: 'auth', description: 'Authentication & Authorization', required: true },
{ name: 'database', description: 'Database connection & ORM', required: true },
{ name: 'crud', description: 'CRUD operations generator', recommended: true },
{ name: 'email', description: 'Email service', recommended: true },
{ name: 'websockets', description: 'Real-time communication', optional: true },
{ name: 'cache', description: 'Caching layer', recommended: true },
{ name: 'logging', description: 'Logging system', required: true },
{ name: 'monitoring', description: 'Monitoring & observability', recommended: true },
{ name: 'notifications', description: 'Push notifications', optional: true },
{ name: 'validation', description: 'Data validation', recommended: true },
{ name: 'testing', description: 'Testing framework', required: true },
{ name: 'docker', description: 'Docker configuration', recommended: true },
{ name: 'ci', description: 'CI/CD pipelines', recommended: true }
];
// Auto-select required modules
this.selectedModules = availableModules
.filter(module => module.required)
.map(module => module.name);
// Auto-select recommended modules (can be customized)
const recommendedModules = availableModules
.filter(module => module.recommended)
.map(module => module.name);
this.selectedModules.push(...recommendedModules);
console.log('β
Selected modules:', this.selectedModules.join(', '));
}
async selectFeatures() {
console.log('\nβοΈ Project features:');
console.log('β
Docker support: enabled');
console.log('β
CI/CD pipelines: enabled');
console.log('β
Monitoring: enabled');
console.log('β
Testing: enabled');
}
async createProject() {
console.log('\nποΈ Creating project structure...');
// Create project directory
if (fs.existsSync(this.projectPath)) {
throw new Error(`Directory ${this.projectName} already exists`);
}
fs.mkdirSync(this.projectPath, { recursive: true });
// Create basic structure
const directories = [
'src',
'src/controllers',
'src/services',
'src/middleware',
'src/routes',
'src/types',
'src/utils',
'config',
'tests',
'docs',
'scripts'
];
directories.forEach(dir => {
fs.mkdirSync(path.join(this.projectPath, dir), { recursive: true });
});
// Generate package.json
await this.generatePackageJson();
// Generate environment files
await this.generateEnvironmentFiles();
// Generate TypeScript config
await this.generateTSConfig();
// Initialize modules
await this.initializeModules();
// Generate main application files
await this.generateAppFiles();
}
async generatePackageJson() {
const packageJson = {
name: this.projectName,
version: '1.0.0',
description: 'Backend application generated with MCP Backend Generator',
main: 'dist/index.js',
scripts: {
'dev': 'tsx watch src/index.ts',
'build': 'tsc',
'start': 'node dist/index.js',
'test': 'jest',
'test:watch': 'jest --watch',
'test:coverage': 'jest --coverage',
'lint': 'eslint src/**/*.ts',
'lint:fix': 'eslint src/**/*.ts --fix',
'format': 'prettier --write src/**/*.ts',
'db:migrate': 'prisma migrate dev',
'db:generate': 'prisma generate',
'db:seed': 'tsx prisma/seeds/seed.ts',
'docker:build': 'docker build -t ' + this.projectName + ' .',
'docker:run': 'docker run -p 3000:3000 ' + this.projectName
},
dependencies: {
'express': '^4.18.2',
'cors': '^2.8.5',
'helmet': '^7.0.0',
'dotenv': '^16.3.1',
'bcrypt': '^5.1.0',
'jsonwebtoken': '^9.0.2',
'joi': '^17.9.2',
'winston': '^3.10.0'
},
devDependencies: {
'@types/node': '^20.5.0',
'@types/express': '^4.17.17',
'@types/cors': '^2.8.13',
'@types/bcrypt': '^5.0.0',
'@types/jsonwebtoken': '^9.0.2',
'@types/jest': '^29.5.4',
'typescript': '^5.1.6',
'tsx': '^3.12.7',
'jest': '^29.6.2',
'ts-jest': '^29.1.1',
'eslint': '^8.47.0',
'@typescript-eslint/parser': '^6.4.0',
'@typescript-eslint/eslint-plugin': '^6.4.0',
'prettier': '^3.0.1'
},
keywords: ['backend', 'api', 'nodejs', 'typescript', 'mcp'],
author: 'MCP Backend Generator',
license: 'MIT'
};
// Add database-specific dependencies
if (this.databaseType === 'postgres') {
packageJson.dependencies['pg'] = '^8.11.0';
packageJson.dependencies['@prisma/client'] = '^5.0.0';
packageJson.devDependencies['@types/pg'] = '^8.10.0';
packageJson.devDependencies['prisma'] = '^5.0.0';
} else if (this.databaseType === 'mysql') {
packageJson.dependencies['mysql2'] = '^3.6.0';
packageJson.dependencies['@prisma/client'] = '^5.0.0';
packageJson.devDependencies['@types/mysql2'] = '^3.0.0';
packageJson.devDependencies['prisma'] = '^5.0.0';
}
fs.writeFileSync(
path.join(this.projectPath, 'package.json'),
JSON.stringify(packageJson, null, 2)
);
}
async generateEnvironmentFiles() {
const envContent = `# Application
NODE_ENV=development
PORT=3000
API_VERSION=v1
# Database
DATABASE_URL="${this.databaseType}://username:password@localhost:${this.databaseType === 'postgres' ? '5432' : '3306'}/${this.projectName}"
${this.databaseType.toUpperCase()}_HOST=localhost
${this.databaseType.toUpperCase()}_PORT=${this.databaseType === 'postgres' ? '5432' : '3306'}
${this.databaseType.toUpperCase()}_${this.databaseType === 'postgres' ? 'DB' : 'DATABASE'}=${this.projectName}
${this.databaseType.toUpperCase()}_USER=username
${this.databaseType.toUpperCase()}_PASSWORD=password
# JWT
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
JWT_EXPIRES_IN=7d
# Email
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
# Redis (if using cache)
REDIS_URL=redis://localhost:6379
# Monitoring
MONITORING_ENABLED=true
PROMETHEUS_PORT=9090
GRAFANA_PORT=3001
# Logging
LOG_LEVEL=info
LOG_FILE=logs/app.log`;
fs.writeFileSync(path.join(this.projectPath, '.env'), envContent);
fs.writeFileSync(path.join(this.projectPath, '.env.example'), envContent);
}
async generateTSConfig() {
const tsConfig = {
compilerOptions: {
target: 'ES2020',
module: 'commonjs',
lib: ['ES2020'],
outDir: './dist',
rootDir: './src',
strict: true,
esModuleInterop: true,
skipLibCheck: true,
forceConsistentCasingInFileNames: true,
resolveJsonModule: true,
declaration: true,
declarationMap: true,
sourceMap: true,
experimentalDecorators: true,
emitDecoratorMetadata: true
},
include: ['src/**/*'],
exclude: ['node_modules', 'dist', 'tests']
};
fs.writeFileSync(
path.join(this.projectPath, 'tsconfig.json'),
JSON.stringify(tsConfig, null, 2)
);
}
async initializeModules() {
console.log('π§ Initializing modules...');
const mcpPath = path.join(__dirname, '..');
const orchestratorPath = path.join(mcpPath, 'orchestrator.js');
if (fs.existsSync(orchestratorPath)) {
// Use the orchestrator to initialize modules
const { Orchestrator } = require(orchestratorPath);
const orchestrator = new Orchestrator();
for (const moduleName of this.selectedModules) {
console.log(` π¦ Initializing ${moduleName}...`);
try {
await orchestrator.initializeModule(moduleName, {
projectPath: this.projectPath,
databaseType: this.databaseType
});
} catch (error) {
console.warn(` β οΈ Warning: Could not initialize ${moduleName}:`, error.message);
}
}
} else {
console.warn('β οΈ Orchestrator not found, skipping module initialization');
}
}
async generateAppFiles() {
// Generate main app file
const appContent = `import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import dotenv from 'dotenv';
import { errorHandler } from './middleware/errorHandler';
import { logger } from './utils/logger';
import routes from './routes';
// Load environment variables
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(helmet());
app.use(cors());
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Routes
app.use('/api/v1', routes);
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'OK', timestamp: new Date().toISOString() });
});
// Error handling
app.use(errorHandler);
// Start server
app.listen(PORT, () => {
logger.info(\`π Server running on port \${PORT}\`);
logger.info(\`π Environment: \${process.env.NODE_ENV}\`);
logger.info(\`π API: http://localhost:\${PORT}/api/v1\`);
});
export default app;`;
fs.writeFileSync(path.join(this.projectPath, 'src/app.ts'), appContent);
// Generate index file
const indexContent = `import app from './app';
// Handle unhandled promise rejections
process.on('unhandledRejection', (err: Error) => {
console.error('Unhandled Promise Rejection:', err);
process.exit(1);
});
// Handle uncaught exceptions
process.on('uncaughtException', (err: Error) => {
console.error('Uncaught Exception:', err);
process.exit(1);
});
export default app;`;
fs.writeFileSync(path.join(this.projectPath, 'src/index.ts'), indexContent);
}
async installDependencies() {
console.log('\nπ¦ Installing dependencies...');
try {
process.chdir(this.projectPath);
execSync('npm install', { stdio: 'inherit' });
} catch (error) {
console.warn('β οΈ Could not install dependencies automatically. Run "npm install" manually.');
}
}
async generateDocumentation() {
const readmeContent = `# ${this.projectName}
Backend application generated with MCP Backend Generator.
## Features
${this.selectedModules.map(module => `- β
${module.charAt(0).toUpperCase() + module.slice(1)}`).join('\n')}
## Quick Start
1. Install dependencies:
\`\`\`bash
npm install
\`\`\`
2. Set up environment variables:
\`\`\`bash
cp .env.example .env
# Edit .env with your configuration
\`\`\`
3. Set up database:
\`\`\`bash
npm run db:migrate
npm run db:seed
\`\`\`
4. Start development server:
\`\`\`bash
npm run dev
\`\`\`
## API Documentation
The API will be available at \`http://localhost:3000/api/v1\`
### Health Check
\`\`\`
GET /health
\`\`\`
## Scripts
- \`npm run dev\` - Start development server
- \`npm run build\` - Build for production
- \`npm run start\` - Start production server
- \`npm test\` - Run tests
- \`npm run lint\` - Lint code
- \`npm run format\` - Format code
## Database
This project uses ${this.databaseType.charAt(0).toUpperCase() + this.databaseType.slice(1)} as the database.
### Migrations
\`\`\`bash
npm run db:migrate # Run migrations
npm run db:generate # Generate Prisma client
npm run db:seed # Seed database
\`\`\`
## Docker
\`\`\`bash
npm run docker:build # Build Docker image
npm run docker:run # Run Docker container
\`\`\`
## Contributing
1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests
5. Submit a pull request
## License
MIT`;
fs.writeFileSync(path.join(this.projectPath, 'README.md'), readmeContent);
}
question(rl, prompt) {
return new Promise((resolve) => {
rl.question(prompt, (answer) => {
resolve(answer.trim());
});
});
}
}
// Run the initializer
if (require.main === module) {
const initializer = new ProjectInitializer();
initializer.init().catch(console.error);
}
module.exports = ProjectInitializer;