Skip to main content
Glama

Advanced Docker Compose Manager

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;
      }
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. It mentions 'plan+apply workflow' which hints at a two-step process, but fails to describe critical behaviors: what 'plan' does (e.g., preview changes), what 'apply' entails (e.g., actual deployment), side effects, error handling, or authentication needs. For a tool with actions like 'destroy' that could be destructive, this lack of transparency is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that front-loads the core purpose ('Manage Docker projects') and key differentiator ('plan+apply workflow'). There is zero wasted text, making it easy for an agent to parse quickly while still conveying essential information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (4 parameters including enums, actions like 'destroy', no output schema, and no annotations), the description is inadequate. It doesn't explain the workflow details, return values, error conditions, or how parameters interact (e.g., 'containers' being required for 'plan'). For an 'advanced' tool with potential destructive operations, more context is needed to ensure safe and correct usage.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, providing good documentation for all parameters. The description adds minimal value beyond the schema—it mentions 'natural language' which might relate to the 'containers' parameter, but doesn't clarify syntax or examples. With high schema coverage, the baseline score of 3 is appropriate, as the description doesn't significantly enhance parameter understanding.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose as 'Manage Docker projects with natural language using plan+apply workflow', which specifies the verb ('manage'), resource ('Docker projects'), and approach ('plan+apply workflow'). It distinguishes from simpler sibling 'docker_compose' by emphasizing 'advanced' capabilities, though it doesn't explicitly contrast with all siblings like 'execute_docker_command' or 'manage_containers'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage through the mention of 'plan+apply workflow', suggesting this tool is for structured Docker project management rather than ad-hoc operations. However, it provides no explicit guidance on when to use this versus alternatives like 'docker_compose', 'execute_docker_command', or 'manage_containers', leaving the agent to infer based on the 'advanced' and 'workflow' cues.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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