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