UNPKG

auto-publishing-mcp-server

Version:

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

421 lines (370 loc) 13.9 kB
/** * Microsoft Azure Integration Tools * Deploy and manage applications on Azure Container Instances, AKS, and Azure Functions */ import { execSync } from 'child_process'; import fs from 'fs/promises'; import path from 'path'; export class AzureTools { constructor(config = {}) { this.subscriptionId = config.subscriptionId || process.env.AZURE_SUBSCRIPTION_ID; this.resourceGroup = config.resourceGroup || process.env.AZURE_RESOURCE_GROUP || 'autopublishing-rg'; this.location = config.location || process.env.AZURE_LOCATION || 'eastus'; this.tenantId = config.tenantId || process.env.AZURE_TENANT_ID; } /** * Deploy to Azure Container Instances (ACI) */ async deployToACI(args) { const { containerName, image, cpu = 1, memory = 1.5, port = 3000, environment = {}, dnsNameLabel, restartPolicy = 'Always' } = args; if (!containerName || !image) { throw new Error('containerName and image are required'); } try { // Build az container create command let createCmd = `az container create --resource-group ${this.resourceGroup} --name ${containerName} --image ${image} --cpu ${cpu} --memory ${memory} --restart-policy ${restartPolicy} --location ${this.location}`; // Add port mapping createCmd += ` --ports ${port}`; // Add DNS name label if provided if (dnsNameLabel) { createCmd += ` --dns-name-label ${dnsNameLabel}`; } // Add environment variables if (Object.keys(environment).length > 0) { const envVars = Object.entries(environment).map(([key, value]) => `${key}=${value}`).join(' '); createCmd += ` --environment-variables ${envVars}`; } // Execute deployment const output = execSync(createCmd, { encoding: 'utf8' }); const result = JSON.parse(output); return { output: `Successfully deployed ${containerName} to Azure Container Instances`, data: { containerName, resourceGroup: this.resourceGroup, location: this.location, image, fqdn: result.ipAddress?.fqdn, ipAddress: result.ipAddress?.ip, status: result.instanceView?.currentState?.state || 'Unknown' } }; } catch (error) { throw new Error(`ACI deployment failed: ${error.message}`); } } /** * Deploy to Azure Kubernetes Service (AKS) */ async deployToAKS(args) { const { clusterName, appName, image, replicas = 1, port = 3000, environment = {}, exposeService = true, serviceType = 'LoadBalancer' } = args; if (!clusterName || !appName || !image) { throw new Error('clusterName, appName, and image are required'); } try { // Get AKS credentials execSync(`az aks get-credentials --resource-group ${this.resourceGroup} --name ${clusterName}`); // Create deployment execSync(`kubectl create deployment ${appName} --image=${image} --replicas=${replicas}`); // Add environment variables if provided if (Object.keys(environment).length > 0) { for (const [key, value] of Object.entries(environment)) { execSync(`kubectl set env deployment/${appName} ${key}=${value}`); } } // Expose service if requested let externalIp = null; if (exposeService) { execSync(`kubectl expose deployment ${appName} --type=${serviceType} --port=80 --target-port=${port}`); if (serviceType === 'LoadBalancer') { // Wait for external IP console.log('Waiting for external IP...'); for (let i = 0; i < 30; i++) { try { const serviceOutput = execSync(`kubectl get service ${appName} -o jsonpath='{.status.loadBalancer.ingress[0].ip}'`, { encoding: 'utf8' }); if (serviceOutput && serviceOutput !== '') { externalIp = serviceOutput; break; } } catch { // Continue waiting } await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds } } } return { output: `Successfully deployed ${appName} to AKS cluster ${clusterName}`, data: { clusterName, resourceGroup: this.resourceGroup, appName, replicas, externalIp, serviceType, status: 'deployed' } }; } catch (error) { throw new Error(`AKS deployment failed: ${error.message}`); } } /** * Deploy Azure Function */ async deployToAzureFunction(args) { const { functionAppName, sourceDir = '.', runtime = 'node', runtimeVersion = '18', storageAccount, environment = {} } = args; if (!functionAppName || !storageAccount) { throw new Error('functionAppName and storageAccount are required'); } try { // Create function app if it doesn't exist try { execSync(`az functionapp show --name ${functionAppName} --resource-group ${this.resourceGroup}`, { encoding: 'utf8' }); } catch { // Function app doesn't exist, create it execSync(`az functionapp create --resource-group ${this.resourceGroup} --consumption-plan-location ${this.location} --runtime ${runtime} --runtime-version ${runtimeVersion} --functions-version 4 --name ${functionAppName} --storage-account ${storageAccount}`); } // Set environment variables if (Object.keys(environment).length > 0) { const envSettings = Object.entries(environment).map(([key, value]) => `${key}=${value}`).join(' '); execSync(`az functionapp config appsettings set --name ${functionAppName} --resource-group ${this.resourceGroup} --settings ${envSettings}`); } // Deploy function code execSync(`cd ${sourceDir} && func azure functionapp publish ${functionAppName}`); // Get function app details const output = execSync(`az functionapp show --name ${functionAppName} --resource-group ${this.resourceGroup}`, { encoding: 'utf8' }); const functionApp = JSON.parse(output); return { output: `Successfully deployed Azure Function ${functionAppName}`, data: { functionAppName, resourceGroup: this.resourceGroup, location: this.location, runtime: `${runtime} ${runtimeVersion}`, url: functionApp.defaultHostName ? `https://${functionApp.defaultHostName}` : null, status: functionApp.state } }; } catch (error) { throw new Error(`Azure Function deployment failed: ${error.message}`); } } /** * Get Azure service status */ async getServiceStatus(args) { const { serviceType, serviceName, clusterName } = args; try { switch (serviceType) { case 'aci': return await this.getACIStatus(serviceName); case 'aks': return await this.getAKSServiceStatus(serviceName, clusterName); case 'function': return await this.getAzureFunctionStatus(serviceName); default: throw new Error(`Unsupported service type: ${serviceType}`); } } catch (error) { throw new Error(`Failed to get service status: ${error.message}`); } } /** * Get ACI container status */ async getACIStatus(containerName) { const output = execSync(`az container show --resource-group ${this.resourceGroup} --name ${containerName}`, { encoding: 'utf8' }); const container = JSON.parse(output); return { output: `ACI container ${containerName} status`, data: { containerName: container.name, resourceGroup: this.resourceGroup, location: container.location, state: container.instanceView?.currentState?.state, ipAddress: container.ipAddress?.ip, fqdn: container.ipAddress?.fqdn, cpu: container.containers[0]?.resources?.requests?.cpu, memory: container.containers[0]?.resources?.requests?.memoryInGB, restartCount: container.instanceView?.currentState?.restartCount || 0 } }; } /** * Get AKS service status */ async getAKSServiceStatus(serviceName, clusterName) { // Get AKS credentials first execSync(`az aks get-credentials --resource-group ${this.resourceGroup} --name ${clusterName}`); const deploymentOutput = execSync(`kubectl get deployment ${serviceName} -o json`, { encoding: 'utf8' }); const deployment = JSON.parse(deploymentOutput); let serviceInfo = null; try { const serviceOutput = execSync(`kubectl get service ${serviceName} -o json`, { encoding: 'utf8' }); serviceInfo = JSON.parse(serviceOutput); } catch { // Service might not exist } return { output: `AKS deployment ${serviceName} status`, data: { serviceName, clusterName, resourceGroup: this.resourceGroup, replicas: { desired: deployment.spec.replicas, available: deployment.status.availableReplicas || 0, ready: deployment.status.readyReplicas || 0 }, status: deployment.status.conditions?.[0]?.type || 'Unknown', externalIp: serviceInfo?.status?.loadBalancer?.ingress?.[0]?.ip, serviceType: serviceInfo?.spec?.type } }; } /** * Get Azure Function status */ async getAzureFunctionStatus(functionAppName) { const output = execSync(`az functionapp show --name ${functionAppName} --resource-group ${this.resourceGroup}`, { encoding: 'utf8' }); const functionApp = JSON.parse(output); return { output: `Azure Function ${functionAppName} status`, data: { functionAppName: functionApp.name, resourceGroup: this.resourceGroup, location: functionApp.location, state: functionApp.state, url: functionApp.defaultHostName ? `https://${functionApp.defaultHostName}` : null, runtime: functionApp.siteConfig?.linuxFxVersion || functionApp.siteConfig?.windowsFxVersion, lastModified: functionApp.lastModifiedTimeUtc } }; } /** * Scale Azure services */ async scaleService(args) { const { serviceType, serviceName, replicas, clusterName } = args; try { switch (serviceType) { case 'aks': // Get AKS credentials first execSync(`az aks get-credentials --resource-group ${this.resourceGroup} --name ${clusterName}`); execSync(`kubectl scale deployment ${serviceName} --replicas=${replicas}`); break; case 'function': // Azure Functions auto-scale, but we can set the plan console.log('Azure Functions auto-scale based on demand'); 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}`); } } /** * Create Azure resources */ async setupResources(args) { const { createResourceGroup = true, createAKSCluster = false, clusterName = 'autopublishing-aks', createStorageAccount = false, storageAccountName = 'autopublishingstorage' } = args; try { const results = []; if (createResourceGroup) { // Create resource group execSync(`az group create --name ${this.resourceGroup} --location ${this.location}`); results.push(`Created resource group ${this.resourceGroup}`); } if (createAKSCluster) { // Create AKS cluster execSync(`az aks create --resource-group ${this.resourceGroup} --name ${clusterName} --node-count 3 --node-vm-size Standard_B2s --location ${this.location} --enable-addons monitoring --generate-ssh-keys`); results.push(`Created AKS cluster ${clusterName}`); } if (createStorageAccount) { // Create storage account for Azure Functions execSync(`az storage account create --name ${storageAccountName} --location ${this.location} --resource-group ${this.resourceGroup} --sku Standard_LRS`); results.push(`Created storage account ${storageAccountName}`); } return { output: 'Azure resources setup completed', data: { subscriptionId: this.subscriptionId, resourceGroup: this.resourceGroup, location: this.location, actions: results } }; } catch (error) { throw new Error(`Resource setup failed: ${error.message}`); } } /** * Build and push container image to Azure Container Registry */ async buildAndPushImage(args) { const { registryName, imageName, sourceDir = '.', tag = 'latest' } = args; if (!registryName || !imageName) { throw new Error('registryName and imageName are required'); } try { // Login to ACR execSync(`az acr login --name ${registryName}`); const fullImageName = `${registryName}.azurecr.io/${imageName}:${tag}`; // Build and push image execSync(`az acr build --registry ${registryName} --image ${imageName}:${tag} ${sourceDir}`); return { output: `Successfully built and pushed image ${fullImageName}`, data: { imageName: fullImageName, registry: `${registryName}.azurecr.io`, tag } }; } catch (error) { throw new Error(`Image build failed: ${error.message}`); } } } export default AzureTools;