Skip to main content
Glama

docker_compose_advanced

Manage Docker Compose projects using natural language with plan and apply workflows for container orchestration.

Instructions

Manage Docker projects with natural language using plan+apply workflow

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
actionYesAction to perform
projectNameYesUnique name of the project
containersNoDescribe containers you want (required for plan)
commandNoSpecial commands

Implementation Reference

  • src/index.ts:1076-1155 (registration)
    Registration of the 'docker_compose_advanced' MCP tool, including input schema definition and the handler function that orchestrates plan/apply/destroy/status actions using supporting manager classes.
      "docker_compose_advanced",
      {
        title: "Advanced Docker Compose Manager",
        description: "Manage Docker projects with natural language using plan+apply workflow",
        inputSchema: {
          action: z.enum(["plan", "apply", "destroy", "status"]).describe("Action to perform"),
          projectName: z.string().describe("Unique name of the project"),
          containers: z.string().optional().describe("Describe containers you want (required for plan)"),
          command: z.enum(["help", "apply", "down", "ps", "quiet", "verbose", "destroy"]).optional().describe("Special commands")
        }
      },
      async ({ action, projectName, containers, command }) => {
        try {
          switch (action) {
            case "plan":
              if (!containers) {
                throw new Error("Container description is required for planning");
              }
              
              const project = await DockerComposeManager.parseNaturalLanguage(containers, projectName);
              DockerComposeManager.setProject(projectName, project);
              
              const currentResources = await ProjectManager.getProjectResources(projectName);
              const plan = await DockerComposeManager.generatePlan(project, currentResources);
              
              return {
                content: [
                  {
                    type: "text",
                    text: `## Docker Compose Manager - Project: ${projectName}\n\n${plan}\n\n### Resources Currently Present:\n**Containers:** ${currentResources.containers.length}\n**Networks:** ${currentResources.networks.length}\n**Volumes:** ${currentResources.volumes.length}`
                  }
                ]
              };
    
            case "apply":
              const applyResult = await DockerComposeManager.applyPlan(projectName);
              return {
                content: [
                  {
                    type: "text",
                    text: applyResult
                  }
                ]
              };
    
            case "destroy":
              const destroyResult = await DockerComposeManager.destroyProject(projectName);
              return {
                content: [
                  {
                    type: "text",
                    text: destroyResult
                  }
                ]
              };
    
            case "status":
              const resources = await ProjectManager.getProjectResources(projectName);
              return {
                content: [
                  {
                    type: "text",
                    text: `## Project Status: ${projectName}\n\n**Containers:** ${resources.containers.length} (${resources.containers.filter(c => c.State === 'running').length} running)\n**Networks:** ${resources.networks.length}\n**Volumes:** ${resources.volumes.length}\n\n### Containers:\n${resources.containers.map(c => `- ${c.Names || c.name}: ${c.State || c.status}`).join('\n') || 'None'}\n\n### Networks:\n${resources.networks.map(n => `- ${n.Name || n.name}`).join('\n') || 'None'}\n\n### Volumes:\n${resources.volumes.map(v => `- ${v.Name || v.name}`).join('\n') || 'None'}`
                  }
                ]
              };
          }
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error with Docker Compose operation: ${error instanceof Error ? error.message : String(error)}`
              }
            ],
            isError: true
          };
        }
      }
    );
  • Input schema using Zod for validating tool parameters: action (plan/apply/destroy/status), projectName, optional containers description, and command.
      action: z.enum(["plan", "apply", "destroy", "status"]).describe("Action to perform"),
      projectName: z.string().describe("Unique name of the project"),
      containers: z.string().optional().describe("Describe containers you want (required for plan)"),
      command: z.enum(["help", "apply", "down", "ps", "quiet", "verbose", "destroy"]).optional().describe("Special commands")
    }
  • Handler function implementing the tool logic: parses natural language for plan, applies/destroys projects, and shows status by calling DockerComposeManager and ProjectManager methods.
      async ({ action, projectName, containers, command }) => {
        try {
          switch (action) {
            case "plan":
              if (!containers) {
                throw new Error("Container description is required for planning");
              }
              
              const project = await DockerComposeManager.parseNaturalLanguage(containers, projectName);
              DockerComposeManager.setProject(projectName, project);
              
              const currentResources = await ProjectManager.getProjectResources(projectName);
              const plan = await DockerComposeManager.generatePlan(project, currentResources);
              
              return {
                content: [
                  {
                    type: "text",
                    text: `## Docker Compose Manager - Project: ${projectName}\n\n${plan}\n\n### Resources Currently Present:\n**Containers:** ${currentResources.containers.length}\n**Networks:** ${currentResources.networks.length}\n**Volumes:** ${currentResources.volumes.length}`
                  }
                ]
              };
    
            case "apply":
              const applyResult = await DockerComposeManager.applyPlan(projectName);
              return {
                content: [
                  {
                    type: "text",
                    text: applyResult
                  }
                ]
              };
    
            case "destroy":
              const destroyResult = await DockerComposeManager.destroyProject(projectName);
              return {
                content: [
                  {
                    type: "text",
                    text: destroyResult
                  }
                ]
              };
    
            case "status":
              const resources = await ProjectManager.getProjectResources(projectName);
              return {
                content: [
                  {
                    type: "text",
                    text: `## Project Status: ${projectName}\n\n**Containers:** ${resources.containers.length} (${resources.containers.filter(c => c.State === 'running').length} running)\n**Networks:** ${resources.networks.length}\n**Volumes:** ${resources.volumes.length}\n\n### Containers:\n${resources.containers.map(c => `- ${c.Names || c.name}: ${c.State || c.status}`).join('\n') || 'None'}\n\n### Networks:\n${resources.networks.map(n => `- ${n.Name || n.name}`).join('\n') || 'None'}\n\n### Volumes:\n${resources.volumes.map(v => `- ${v.Name || v.name}`).join('\n') || 'None'}`
                  }
                ]
              };
          }
        } catch (error) {
          return {
            content: [
              {
                type: "text",
                text: `Error with Docker Compose operation: ${error instanceof Error ? error.message : String(error)}`
              }
            ],
            isError: true
          };
        }
      }
    );
  • Core helper class providing advanced Docker Compose functionality: natural language parsing to service configs, plan generation (diff current vs desired), apply (create/start resources), and destroy.
    class DockerComposeManager {
      private static projects: Map<string, any> = new Map();
    
      static async parseNaturalLanguage(description: string, projectName: string): Promise<ComposeProject> {
        // Advanced natural language parsing for multi-service deployments
        const services: Record<string, ServiceConfig> = {};
        const networks: Record<string, any> = {};
        const volumes: Record<string, any> = {};
    
        // Parse common patterns
        if (description.includes('wordpress') || description.includes('wp')) {
          services.wordpress = {
            image: 'wordpress:latest',
            ports: ['9000:80'],
            environment: {
              WORDPRESS_DB_HOST: 'mysql',
              WORDPRESS_DB_USER: 'wordpress',
              WORDPRESS_DB_PASSWORD: 'wordpress',
              WORDPRESS_DB_NAME: 'wordpress'
            },
            depends_on: ['mysql']
          };
          
          services.mysql = {
            image: 'mysql:8.0',
            environment: {
              MYSQL_DATABASE: 'wordpress',
              MYSQL_USER: 'wordpress',
              MYSQL_PASSWORD: 'wordpress',
              MYSQL_ROOT_PASSWORD: 'rootpassword'
            },
            volumes: ['mysql-data:/var/lib/mysql']
          };
          
          volumes['mysql-data'] = {};
        }
    
        if (description.includes('nginx')) {
          const port = description.match(/port\s+(\d+)/)?.[1] || '80';
          services.nginx = {
            image: 'nginx:latest',
            ports: [`${port}:80`]
          };
        }
    
        if (description.includes('redis')) {
          services.redis = {
            image: 'redis:alpine',
            ports: ['6379:6379']
          };
        }
    
        if (description.includes('postgres')) {
          services.postgres = {
            image: 'postgres:15',
            environment: {
              POSTGRES_DB: 'myapp',
              POSTGRES_USER: 'user',
              POSTGRES_PASSWORD: 'password'
            },
            volumes: ['postgres-data:/var/lib/postgresql/data']
          };
          
          volumes['postgres-data'] = {};
        }
    
        return { name: projectName, services, networks, volumes };
      }
    
      static async generatePlan(project: any, currentResources: DockerProject): Promise<string> {
        const actions: string[] = [];
    
        // Compare desired vs current state
        const currentContainerNames = currentResources.containers.map(c => c.Names || c.name);
        const desiredServiceNames = Object.keys(project.services);
    
        // Plan container actions
        for (const serviceName of desiredServiceNames) {
          const containerName = `${project.name}-${serviceName}`;
          if (!currentContainerNames.some(name => name.includes(serviceName))) {
            actions.push(`CREATE container ${containerName} from ${project.services[serviceName].image}`);
          }
        }
    
        // Plan volume actions
        const currentVolumeNames = currentResources.volumes.map(v => v.Name || v.name);
        const desiredVolumeNames = Object.keys(project.volumes || {});
        
        for (const volumeName of desiredVolumeNames) {
          const fullVolumeName = `${project.name}-${volumeName}`;
          if (!currentVolumeNames.includes(fullVolumeName)) {
            actions.push(`CREATE volume ${fullVolumeName}`);
          }
        }
    
        // Plan network actions if needed
        if (Object.keys(project.services).length > 1) {
          const networkName = `${project.name}-network`;
          const currentNetworkNames = currentResources.networks.map(n => n.Name || n.name);
          if (!currentNetworkNames.includes(networkName)) {
            actions.push(`CREATE network ${networkName}`);
          }
        }
    
        if (actions.length === 0) {
          return "No changes to make; project is up-to-date.";
        }
    
        return `## Plan\n\nI plan to take the following actions:\n\n${actions.map((action, i) => `${i + 1}. ${action}`).join('\n')}\n\nRespond \`apply\` to apply this plan. Otherwise, provide feedback and I will present you with an updated plan.`;
      }
    
      static async applyPlan(projectName: string): Promise<string> {
        const project = this.projects.get(projectName);
        if (!project) {
          throw new Error(`No plan found for project ${projectName}`);
        }
    
        const results: string[] = [];
    
        try {
          // Create network first if needed
          if (Object.keys(project.services).length > 1) {
            const networkName = `${projectName}-network`;
            const networkCmd = ProjectManager.addProjectLabel(`docker network create ${networkName}`, projectName);
            await executeDockerCommand(networkCmd);
            results.push(`✅ Created network ${networkName}`);
          }
    
          // Create volumes
          for (const volumeName of Object.keys(project.volumes || {})) {
            const fullVolumeName = `${projectName}-${volumeName}`;
            const volumeCmd = ProjectManager.addProjectLabel(`docker volume create ${fullVolumeName}`, projectName);
            await executeDockerCommand(volumeCmd);
            results.push(`✅ Created volume ${fullVolumeName}`);
          }
    
          // Create and start containers
          for (const [serviceName, config] of Object.entries(project.services) as [string, ServiceConfig][]) {
            const containerName = `${projectName}-${serviceName}`;
            let runCmd = `docker run -d --name ${containerName}`;
    
            // Add ports
            if (config.ports) {
              config.ports.forEach((port: string) => {
                runCmd += ` -p ${port}`;
              });
            }
    
            // Add environment variables
            if (config.environment) {
              Object.entries(config.environment).forEach(([key, value]) => {
                runCmd += ` -e ${key}=${value}`;
              });
            }
    
            // Add volumes
            if (config.volumes) {
              config.volumes.forEach((volume: string) => {
                if (volume.includes(':')) {
                  // Replace volume name with project-prefixed name
                  const [volumeName, mountPoint] = volume.split(':');
                  const fullVolumeName = `${projectName}-${volumeName}`;
                  runCmd += ` -v ${fullVolumeName}:${mountPoint}`;
                } else {
                  runCmd += ` -v ${volume}`;
                }
              });
            }
    
            // Add network
            if (Object.keys(project.services).length > 1) {
              runCmd += ` --network ${projectName}-network`;
            }
    
            runCmd += ` ${config.image}`;
    
            const labeledCmd = ProjectManager.addProjectLabel(runCmd, projectName);
            await executeDockerCommand(labeledCmd);
            results.push(`✅ Created and started container ${containerName}`);
          }
    
          return `## Apply Complete\n\n${results.join('\n')}\n\nProject ${projectName} has been successfully deployed!`;
        } catch (error) {
          throw new Error(`Failed to apply plan: ${error instanceof Error ? error.message : String(error)}`);
        }
      }
    
      static async destroyProject(projectName: string): Promise<string> {
        const resources = await ProjectManager.getProjectResources(projectName);
        const results: string[] = [];
    
        // Stop and remove containers
        for (const container of resources.containers) {
          try {
            await executeDockerCommand(`docker stop ${container.ID || container.id}`);
            await executeDockerCommand(`docker rm ${container.ID || container.id}`);
            results.push(`✅ Removed container ${container.Names || container.name}`);
          } catch (error) {
            results.push(`❌ Failed to remove container ${container.Names || container.name}`);
          }
        }
    
        // Remove volumes
        for (const volume of resources.volumes) {
          try {
            await executeDockerCommand(`docker volume rm ${volume.Name || volume.name}`);
            results.push(`✅ Removed volume ${volume.Name || volume.name}`);
          } catch (error) {
            results.push(`❌ Failed to remove volume ${volume.Name || volume.name}`);
          }
        }
    
        // Remove networks
        for (const network of resources.networks) {
          try {
            await executeDockerCommand(`docker network rm ${network.Name || network.name}`);
            results.push(`✅ Removed network ${network.Name || network.name}`);
          } catch (error) {
            results.push(`❌ Failed to remove network ${network.Name || network.name}`);
          }
        }
    
        return `## Destroy Complete\n\n${results.join('\n')}\n\nProject ${projectName} has been destroyed.`;
      }
    
      static setProject(projectName: string, project: any): void {
        this.projects.set(projectName, project);
      }
    }
  • Helper for project-based resource tracking: labels resources with project name, lists project-specific resources, adds labels to docker commands.
    class ProjectManager {
      private static getProjectLabel(projectName: string): string {
        return `mcp-server-docker.project=${projectName}`;
      }
    
      static async getProjectResources(projectName: string): Promise<DockerProject> {
        const label = this.getProjectLabel(projectName);
        
        try {
          const [containers, networks, volumes] = await Promise.all([
            executeDockerCommand(`docker ps -a --filter "label=${label}" --format "{{json .}}"`).then(r => 
              r.stdout.trim().split('\n').filter(line => line).map(line => JSON.parse(line))
            ).catch(() => []),
            executeDockerCommand(`docker network ls --filter "label=${label}" --format "{{json .}}"`).then(r => 
              r.stdout.trim().split('\n').filter(line => line).map(line => JSON.parse(line))
            ).catch(() => []),
            executeDockerCommand(`docker volume ls --filter "label=${label}" --format "{{json .}}"`).then(r => 
              r.stdout.trim().split('\n').filter(line => line).map(line => JSON.parse(line))
            ).catch(() => [])
          ]);
    
          return { name: projectName, containers, networks, volumes };
        } catch (error) {
          return { name: projectName, containers: [], networks: [], volumes: [] };
        }
      }
    
      static addProjectLabel(command: string, projectName: string): string {
        const label = this.getProjectLabel(projectName);
        
        // Add label to Docker run commands
        if (command.includes('docker run') || command.includes('docker create')) {
          return command.replace(/(docker (?:run|create))/, `$1 --label "${label}"`);
        }
        
        // Add label to Docker network create commands
        if (command.includes('docker network create')) {
          return command.replace(/(docker network create)/, `$1 --label "${label}"`);
        }
        
        // Add label to Docker volume create commands
        if (command.includes('docker volume create')) {
          return command.replace(/(docker volume create)/, `$1 --label "${label}"`);
        }
        
        return command;
      }
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TauqeerAhmad5201/docker-mcp-extension'

If you have feedback or need assistance with the MCP directory API, please join our Discord server