UNPKG

auto-publishing-mcp-server

Version:

Enterprise-grade MCP Server for Auto-Publishing with pre-publish validation, multi-cloud deployment, and monitoring

468 lines (417 loc) 13.4 kB
/** * AWS Integration Tools * Deploy and manage applications on AWS ECS, EKS, and Lambda */ import { execSync } from 'child_process'; import fs from 'fs/promises'; import path from 'path'; export class AWSTools { constructor(config = {}) { this.region = config.region || process.env.AWS_DEFAULT_REGION || 'us-east-1'; this.accessKeyId = config.accessKeyId || process.env.AWS_ACCESS_KEY_ID; this.secretAccessKey = config.secretAccessKey || process.env.AWS_SECRET_ACCESS_KEY; this.profileName = config.profileName || process.env.AWS_PROFILE || 'default'; } /** * Deploy to AWS ECS */ async deployToECS(args) { const { serviceName, clusterName = 'default', image, taskDefinition, desiredCount = 1, cpu = '256', memory = '512', port = 3000, environment = {} } = args; if (!serviceName || !image) { throw new Error('serviceName and image are required'); } try { // Create or update task definition const taskDefArn = await this.createTaskDefinition({ family: serviceName, image, cpu, memory, port, environment }); // Create or update service const serviceArn = await this.createOrUpdateService({ serviceName, clusterName, taskDefinition: taskDefArn, desiredCount }); // Wait for deployment to complete await this.waitForServiceStable(serviceName, clusterName); return { output: `Successfully deployed ${serviceName} to ECS`, data: { serviceName, clusterName, taskDefinition: taskDefArn, serviceArn, status: 'deployed' } }; } catch (error) { throw new Error(`ECS deployment failed: ${error.message}`); } } /** * Deploy to AWS EKS */ async deployToEKS(args) { const { clusterName, namespace = 'default', appName, image, replicas = 1, port = 3000, environment = {} } = args; if (!clusterName || !appName || !image) { throw new Error('clusterName, appName, and image are required'); } try { // Update kubeconfig for EKS cluster execSync(`aws eks update-kubeconfig --region ${this.region} --name ${clusterName}`); // Create Kubernetes manifests const deployment = this.createEKSDeploymentManifest({ appName, image, replicas, port, environment }); const service = this.createEKSServiceManifest({ appName, port }); // Apply manifests const deploymentFile = `/tmp/${appName}-deployment.yaml`; const serviceFile = `/tmp/${appName}-service.yaml`; await fs.writeFile(deploymentFile, deployment); await fs.writeFile(serviceFile, service); execSync(`kubectl apply -f ${deploymentFile} -n ${namespace}`); execSync(`kubectl apply -f ${serviceFile} -n ${namespace}`); // Wait for deployment execSync(`kubectl wait --for=condition=available --timeout=300s deployment/${appName} -n ${namespace}`); return { output: `Successfully deployed ${appName} to EKS cluster ${clusterName}`, data: { clusterName, namespace, appName, replicas, status: 'deployed' } }; } catch (error) { throw new Error(`EKS deployment failed: ${error.message}`); } } /** * Deploy AWS Lambda function */ async deployToLambda(args) { const { functionName, zipFile, handler = 'index.handler', runtime = 'nodejs18.x', role, environment = {}, timeout = 30, memorySize = 128 } = args; if (!functionName || !zipFile) { throw new Error('functionName and zipFile are required'); } try { // Check if function exists let functionExists = false; try { execSync(`aws lambda get-function --function-name ${functionName} --region ${this.region}`); functionExists = true; } catch { // Function doesn't exist } if (functionExists) { // Update function code execSync(`aws lambda update-function-code --function-name ${functionName} --zip-file fileb://${zipFile} --region ${this.region}`); // Update function configuration const envVars = Object.entries(environment).map(([key, value]) => `${key}=${value}`).join(','); if (envVars) { execSync(`aws lambda update-function-configuration --function-name ${functionName} --environment Variables='{${envVars}}' --region ${this.region}`); } } else { // Create new function const envVars = Object.entries(environment).map(([key, value]) => `${key}=${value}`).join(','); let createCmd = `aws lambda create-function --function-name ${functionName} --runtime ${runtime} --role ${role} --handler ${handler} --zip-file fileb://${zipFile} --timeout ${timeout} --memory-size ${memorySize} --region ${this.region}`; if (envVars) { createCmd += ` --environment Variables='{${envVars}}'`; } execSync(createCmd); } return { output: `Successfully deployed Lambda function ${functionName}`, data: { functionName, runtime, handler, status: functionExists ? 'updated' : 'created' } }; } catch (error) { throw new Error(`Lambda deployment failed: ${error.message}`); } } /** * Get AWS service status */ async getServiceStatus(args) { const { serviceType, serviceName, clusterName } = args; try { switch (serviceType) { case 'ecs': return await this.getECSServiceStatus(serviceName, clusterName); case 'eks': return await this.getEKSServiceStatus(serviceName, clusterName); case 'lambda': return await this.getLambdaStatus(serviceName); default: throw new Error(`Unsupported service type: ${serviceType}`); } } catch (error) { throw new Error(`Failed to get service status: ${error.message}`); } } /** * Create ECS task definition */ async createTaskDefinition(params) { const { family, image, cpu, memory, port, environment } = params; const taskDef = { family, networkMode: 'awsvpc', requiresCompatibilities: ['FARGATE'], cpu, memory, executionRoleArn: `arn:aws:iam::${await this.getAccountId()}:role/ecsTaskExecutionRole`, containerDefinitions: [{ name: family, image, portMappings: [{ containerPort: port, protocol: 'tcp' }], environment: Object.entries(environment).map(([name, value]) => ({ name, value: String(value) })), logConfiguration: { logDriver: 'awslogs', options: { 'awslogs-group': `/ecs/${family}`, 'awslogs-region': this.region, 'awslogs-stream-prefix': 'ecs' } } }] }; const taskDefFile = `/tmp/${family}-task-def.json`; await fs.writeFile(taskDefFile, JSON.stringify(taskDef, null, 2)); const output = execSync(`aws ecs register-task-definition --cli-input-json file://${taskDefFile} --region ${this.region}`, { encoding: 'utf8' }); const result = JSON.parse(output); return result.taskDefinition.taskDefinitionArn; } /** * Create or update ECS service */ async createOrUpdateService(params) { const { serviceName, clusterName, taskDefinition, desiredCount } = params; try { // Try to update existing service const output = execSync(`aws ecs update-service --cluster ${clusterName} --service ${serviceName} --task-definition ${taskDefinition} --desired-count ${desiredCount} --region ${this.region}`, { encoding: 'utf8' }); const result = JSON.parse(output); return result.service.serviceArn; } catch { // Service doesn't exist, create it const output = execSync(`aws ecs create-service --cluster ${clusterName} --service-name ${serviceName} --task-definition ${taskDefinition} --desired-count ${desiredCount} --launch-type FARGATE --network-configuration "awsvpcConfiguration={subnets=[subnet-12345],securityGroups=[sg-12345],assignPublicIp=ENABLED}" --region ${this.region}`, { encoding: 'utf8' }); const result = JSON.parse(output); return result.service.serviceArn; } } /** * Wait for ECS service to be stable */ async waitForServiceStable(serviceName, clusterName) { execSync(`aws ecs wait services-stable --cluster ${clusterName} --services ${serviceName} --region ${this.region}`); } /** * Create EKS deployment manifest */ createEKSDeploymentManifest(params) { const { appName, image, replicas, port, environment } = params; return ` apiVersion: apps/v1 kind: Deployment metadata: name: ${appName} labels: app: ${appName} spec: replicas: ${replicas} selector: matchLabels: app: ${appName} template: metadata: labels: app: ${appName} spec: containers: - name: ${appName} image: ${image} ports: - containerPort: ${port} env: ${Object.entries(environment).map(([key, value]) => ` - name: ${key}\n value: "${value}"`).join('\n')} resources: requests: memory: "128Mi" cpu: "100m" limits: memory: "256Mi" cpu: "200m" `; } /** * Create EKS service manifest */ createEKSServiceManifest(params) { const { appName, port } = params; return ` apiVersion: v1 kind: Service metadata: name: ${appName} labels: app: ${appName} spec: selector: app: ${appName} ports: - port: 80 targetPort: ${port} protocol: TCP type: LoadBalancer `; } /** * Get ECS service status */ async getECSServiceStatus(serviceName, clusterName) { const output = execSync(`aws ecs describe-services --cluster ${clusterName} --services ${serviceName} --region ${this.region}`, { encoding: 'utf8' }); const result = JSON.parse(output); const service = result.services[0]; return { output: `ECS service ${serviceName} status`, data: { serviceName, clusterName, status: service.status, runningCount: service.runningCount, pendingCount: service.pendingCount, desiredCount: service.desiredCount, taskDefinition: service.taskDefinition } }; } /** * Get EKS service status */ async getEKSServiceStatus(serviceName, namespace = 'default') { const output = execSync(`kubectl get deployment ${serviceName} -n ${namespace} -o json`, { encoding: 'utf8' }); const deployment = JSON.parse(output); return { output: `EKS deployment ${serviceName} status`, data: { name: deployment.metadata.name, namespace: deployment.metadata.namespace, replicas: { desired: deployment.spec.replicas, available: deployment.status.availableReplicas || 0, ready: deployment.status.readyReplicas || 0 }, status: deployment.status.conditions?.[0]?.type || 'Unknown' } }; } /** * Get Lambda function status */ async getLambdaStatus(functionName) { const output = execSync(`aws lambda get-function --function-name ${functionName} --region ${this.region}`, { encoding: 'utf8' }); const result = JSON.parse(output); return { output: `Lambda function ${functionName} status`, data: { functionName: result.Configuration.FunctionName, runtime: result.Configuration.Runtime, state: result.Configuration.State, lastModified: result.Configuration.LastModified, memorySize: result.Configuration.MemorySize, timeout: result.Configuration.Timeout } }; } /** * Get AWS account ID */ async getAccountId() { try { const output = execSync('aws sts get-caller-identity --query Account --output text', { encoding: 'utf8' }); return output.trim(); } catch (error) { throw new Error('Failed to get AWS account ID. Please check AWS credentials.'); } } /** * Scale AWS service */ async scaleService(args) { const { serviceType, serviceName, clusterName, replicas } = args; try { switch (serviceType) { case 'ecs': execSync(`aws ecs update-service --cluster ${clusterName} --service ${serviceName} --desired-count ${replicas} --region ${this.region}`); break; case 'eks': execSync(`kubectl scale deployment ${serviceName} --replicas=${replicas}`); break; default: throw new Error(`Scaling not supported for service type: ${serviceType}`); } return { output: `Successfully scaled ${serviceName} to ${replicas} replicas`, data: { serviceName, serviceType, replicas, timestamp: new Date().toISOString() } }; } catch (error) { throw new Error(`Failed to scale service: ${error.message}`); } } } export default AWSTools;