Skip to main content
Glama

Unrestricted Development MCP Server

docker.ts25.1 kB
import { z } from 'zod'; import { exec } from 'child_process'; import { promisify } from 'util'; const execAsync = promisify(exec); /** * Docker Tools * Provides comprehensive Docker operations for autonomous container management * Enables full container orchestration and debugging workflows */ // ========== HELPER TYPE ========== type ToolResponse = { content: Array<{ type: "text"; text: string }>; isError?: boolean; }; // Helper function to execute docker commands async function executeDockerCommand(command: string, cwd?: string): Promise<ToolResponse> { try { const { stdout, stderr } = await execAsync(command, { cwd: cwd || process.cwd(), shell: '/bin/bash', maxBuffer: 10 * 1024 * 1024, // 10MB buffer for logs timeout: 60000 // 60 second timeout for builds }); return { content: [ { type: "text" as const, text: JSON.stringify({ success: true, command: command, stdout: stdout.trim(), stderr: stderr.trim(), cwd: cwd || process.cwd() }, null, 2) } ] }; } catch (error: any) { return { content: [ { type: "text" as const, text: JSON.stringify({ success: false, command: command, stdout: error.stdout?.trim() || '', stderr: error.stderr?.trim() || error.message, exitCode: error.code || 1, cwd: cwd || process.cwd() }, null, 2) } ], isError: true }; } } // Helper function to detect docker-compose or docker compose (V2) async function getDockerComposeCommand(): Promise<string> { try { // Try docker compose (V2) first await execAsync('docker compose version', { timeout: 5000 }); return 'docker compose'; } catch { // Fall back to docker-compose (V1) return 'docker-compose'; } } // Cache for docker compose command to avoid repeated detection let cachedDockerComposeCmd: string | null = null; async function getComposeCmd(): Promise<string> { if (cachedDockerComposeCmd) { return cachedDockerComposeCmd; } cachedDockerComposeCmd = await getDockerComposeCommand(); return cachedDockerComposeCmd; } // ========== TOOL SCHEMAS ========== export const dockerPsSchema = z.object({ all: z.boolean().optional().default(false).describe('Show all containers (default shows just running)'), filter: z.string().optional().describe('Filter output based on conditions (e.g., "status=running")'), format: z.enum(['table', 'json']).optional().default('table').describe('Output format'), cwd: z.string().optional().describe('Working directory') }); export const dockerLogsSchema = z.object({ container: z.string().describe('Container name or ID'), tail: z.number().optional().describe('Number of lines to show from the end of logs'), follow: z.boolean().optional().default(false).describe('Follow log output (not recommended for MCP)'), since: z.string().optional().describe('Show logs since timestamp (e.g., "2023-01-01T00:00:00")'), timestamps: z.boolean().optional().default(false).describe('Show timestamps'), cwd: z.string().optional().describe('Working directory') }); export const dockerExecSchema = z.object({ container: z.string().describe('Container name or ID'), command: z.string().describe('Command to execute'), workdir: z.string().optional().describe('Working directory inside container'), user: z.string().optional().describe('User to run command as'), env: z.record(z.string()).optional().describe('Environment variables'), cwd: z.string().optional().describe('Working directory') }); export const dockerStartSchema = z.object({ containers: z.union([z.string(), z.array(z.string())]).describe('Container name(s) or ID(s)'), cwd: z.string().optional().describe('Working directory') }); export const dockerStopSchema = z.object({ containers: z.union([z.string(), z.array(z.string())]).describe('Container name(s) or ID(s)'), timeout: z.number().optional().describe('Seconds to wait before killing container'), cwd: z.string().optional().describe('Working directory') }); export const dockerRestartSchema = z.object({ containers: z.union([z.string(), z.array(z.string())]).describe('Container name(s) or ID(s)'), timeout: z.number().optional().describe('Seconds to wait before killing container'), cwd: z.string().optional().describe('Working directory') }); export const dockerInspectSchema = z.object({ target: z.string().describe('Container, image, network, or volume name/ID'), type: z.enum(['container', 'image', 'network', 'volume']).optional().describe('Type of object to inspect'), cwd: z.string().optional().describe('Working directory') }); export const dockerBuildSchema = z.object({ path: z.string().describe('Build context path (directory containing Dockerfile)'), tag: z.string().optional().describe('Name and optionally a tag (format: "name:tag")'), dockerfile: z.string().optional().describe('Name of Dockerfile (default: Dockerfile)'), buildArgs: z.record(z.string()).optional().describe('Build-time variables'), target: z.string().optional().describe('Set target build stage'), noCache: z.boolean().optional().default(false).describe('Do not use cache when building'), cwd: z.string().optional().describe('Working directory') }); export const dockerImagesSchema = z.object({ filter: z.string().optional().describe('Filter images (e.g., "reference=node:*")'), all: z.boolean().optional().default(false).describe('Show all images (default hides intermediate)'), format: z.enum(['table', 'json']).optional().default('table').describe('Output format'), cwd: z.string().optional().describe('Working directory') }); export const dockerPullSchema = z.object({ image: z.string().describe('Image name and tag (e.g., "nginx:latest")'), allTags: z.boolean().optional().default(false).describe('Download all tagged images'), cwd: z.string().optional().describe('Working directory') }); export const dockerComposeUpSchema = z.object({ detach: z.boolean().optional().default(true).describe('Detached mode: run in background'), build: z.boolean().optional().default(false).describe('Build images before starting'), services: z.array(z.string()).optional().describe('Only start specific services'), file: z.string().optional().describe('Path to compose file (default: docker-compose.yml)'), cwd: z.string().optional().describe('Working directory (where docker-compose.yml is located)') }); export const dockerComposeDownSchema = z.object({ volumes: z.boolean().optional().default(false).describe('Remove named volumes'), removeOrphans: z.boolean().optional().default(false).describe('Remove containers for services not in compose file'), file: z.string().optional().describe('Path to compose file'), cwd: z.string().optional().describe('Working directory') }); export const dockerComposePsSchema = z.object({ services: z.array(z.string()).optional().describe('Only show specific services'), all: z.boolean().optional().default(false).describe('Show all stopped containers'), file: z.string().optional().describe('Path to compose file'), cwd: z.string().optional().describe('Working directory') }); export const dockerComposeLogsSchema = z.object({ services: z.array(z.string()).optional().describe('Only show logs for specific services'), tail: z.number().optional().describe('Number of lines to show from end of logs'), since: z.string().optional().describe('Show logs since timestamp'), timestamps: z.boolean().optional().default(false).describe('Show timestamps'), file: z.string().optional().describe('Path to compose file'), cwd: z.string().optional().describe('Working directory') }); export const dockerRmSchema = z.object({ containers: z.union([z.string(), z.array(z.string())]).describe('Container name(s) or ID(s) to remove'), force: z.boolean().optional().default(false).describe('Force removal of running containers'), volumes: z.boolean().optional().default(false).describe('Remove anonymous volumes'), cwd: z.string().optional().describe('Working directory') }); export const dockerRmiSchema = z.object({ images: z.union([z.string(), z.array(z.string())]).describe('Image name(s) or ID(s) to remove'), force: z.boolean().optional().default(false).describe('Force removal'), cwd: z.string().optional().describe('Working directory') }); // ========== TOOL IMPLEMENTATIONS ========== export async function dockerPs(args: z.infer<typeof dockerPsSchema>): Promise<ToolResponse> { const allFlag = args.all ? '-a' : ''; const filterFlag = args.filter ? `--filter "${args.filter}"` : ''; const formatFlag = args.format === 'json' ? '--format "{{json .}}"' : '--format "table {{.ID}}\\t{{.Image}}\\t{{.Status}}\\t{{.Names}}\\t{{.Ports}}"'; return executeDockerCommand(`docker ps ${allFlag} ${filterFlag} ${formatFlag}`.trim(), args.cwd); } export async function dockerLogs(args: z.infer<typeof dockerLogsSchema>): Promise<ToolResponse> { const tailFlag = args.tail ? `--tail ${args.tail}` : ''; const followFlag = args.follow ? '-f' : ''; const sinceFlag = args.since ? `--since ${args.since}` : ''; const timestampsFlag = args.timestamps ? '-t' : ''; return executeDockerCommand( `docker logs ${tailFlag} ${followFlag} ${sinceFlag} ${timestampsFlag} ${args.container}`.trim(), args.cwd ); } export async function dockerExec(args: z.infer<typeof dockerExecSchema>): Promise<ToolResponse> { const workdirFlag = args.workdir ? `-w ${args.workdir}` : ''; const userFlag = args.user ? `-u ${args.user}` : ''; const envFlags = args.env ? Object.entries(args.env).map(([key, value]) => `-e ${key}="${value}"`).join(' ') : ''; // Escape the command for shell execution const escapedCommand = args.command.replace(/"/g, '\\"'); return executeDockerCommand( `docker exec ${workdirFlag} ${userFlag} ${envFlags} ${args.container} sh -c "${escapedCommand}"`.trim(), args.cwd ); } export async function dockerStart(args: z.infer<typeof dockerStartSchema>): Promise<ToolResponse> { const containers = Array.isArray(args.containers) ? args.containers.join(' ') : args.containers; return executeDockerCommand(`docker start ${containers}`, args.cwd); } export async function dockerStop(args: z.infer<typeof dockerStopSchema>): Promise<ToolResponse> { const containers = Array.isArray(args.containers) ? args.containers.join(' ') : args.containers; const timeoutFlag = args.timeout ? `-t ${args.timeout}` : ''; return executeDockerCommand(`docker stop ${timeoutFlag} ${containers}`.trim(), args.cwd); } export async function dockerRestart(args: z.infer<typeof dockerRestartSchema>): Promise<ToolResponse> { const containers = Array.isArray(args.containers) ? args.containers.join(' ') : args.containers; const timeoutFlag = args.timeout ? `-t ${args.timeout}` : ''; return executeDockerCommand(`docker restart ${timeoutFlag} ${containers}`.trim(), args.cwd); } export async function dockerInspect(args: z.infer<typeof dockerInspectSchema>): Promise<ToolResponse> { const typeFlag = args.type ? `--type ${args.type}` : ''; return executeDockerCommand(`docker inspect ${typeFlag} ${args.target}`.trim(), args.cwd); } export async function dockerBuild(args: z.infer<typeof dockerBuildSchema>): Promise<ToolResponse> { const tagFlag = args.tag ? `-t ${args.tag}` : ''; const dockerfileFlag = args.dockerfile ? `-f ${args.dockerfile}` : ''; const buildArgsFlags = args.buildArgs ? Object.entries(args.buildArgs).map(([key, value]) => `--build-arg ${key}="${value}"`).join(' ') : ''; const targetFlag = args.target ? `--target ${args.target}` : ''; const noCacheFlag = args.noCache ? '--no-cache' : ''; return executeDockerCommand( `docker build ${tagFlag} ${dockerfileFlag} ${buildArgsFlags} ${targetFlag} ${noCacheFlag} ${args.path}`.trim(), args.cwd ); } export async function dockerImages(args: z.infer<typeof dockerImagesSchema>): Promise<ToolResponse> { const allFlag = args.all ? '-a' : ''; const filterFlag = args.filter ? `--filter "${args.filter}"` : ''; const formatFlag = args.format === 'json' ? '--format "{{json .}}"' : '--format "table {{.Repository}}\\t{{.Tag}}\\t{{.ID}}\\t{{.Size}}"'; return executeDockerCommand(`docker images ${allFlag} ${filterFlag} ${formatFlag}`.trim(), args.cwd); } export async function dockerPull(args: z.infer<typeof dockerPullSchema>): Promise<ToolResponse> { const allTagsFlag = args.allTags ? '-a' : ''; return executeDockerCommand(`docker pull ${allTagsFlag} ${args.image}`.trim(), args.cwd); } export async function dockerComposeUp(args: z.infer<typeof dockerComposeUpSchema>): Promise<ToolResponse> { const detachFlag = args.detach ? '-d' : ''; const buildFlag = args.build ? '--build' : ''; const services = args.services ? args.services.join(' ') : ''; const fileFlag = args.file ? `-f ${args.file}` : ''; const composeCmd = await getComposeCmd(); return executeDockerCommand( `${composeCmd} ${fileFlag} up ${detachFlag} ${buildFlag} ${services}`.trim(), args.cwd ); } export async function dockerComposeDown(args: z.infer<typeof dockerComposeDownSchema>): Promise<ToolResponse> { const volumesFlag = args.volumes ? '-v' : ''; const orphansFlag = args.removeOrphans ? '--remove-orphans' : ''; const fileFlag = args.file ? `-f ${args.file}` : ''; const composeCmd = await getComposeCmd(); return executeDockerCommand( `${composeCmd} ${fileFlag} down ${volumesFlag} ${orphansFlag}`.trim(), args.cwd ); } export async function dockerComposePs(args: z.infer<typeof dockerComposePsSchema>): Promise<ToolResponse> { const allFlag = args.all ? '-a' : ''; const services = args.services ? args.services.join(' ') : ''; const fileFlag = args.file ? `-f ${args.file}` : ''; const composeCmd = await getComposeCmd(); return executeDockerCommand( `${composeCmd} ${fileFlag} ps ${allFlag} ${services}`.trim(), args.cwd ); } export async function dockerComposeLogs(args: z.infer<typeof dockerComposeLogsSchema>): Promise<ToolResponse> { const tailFlag = args.tail ? `--tail ${args.tail}` : ''; const sinceFlag = args.since ? `--since ${args.since}` : ''; const timestampsFlag = args.timestamps ? '-t' : ''; const services = args.services ? args.services.join(' ') : ''; const fileFlag = args.file ? `-f ${args.file}` : ''; const composeCmd = await getComposeCmd(); return executeDockerCommand( `${composeCmd} ${fileFlag} logs ${tailFlag} ${sinceFlag} ${timestampsFlag} ${services}`.trim(), args.cwd ); } export async function dockerRm(args: z.infer<typeof dockerRmSchema>): Promise<ToolResponse> { const containers = Array.isArray(args.containers) ? args.containers.join(' ') : args.containers; const forceFlag = args.force ? '-f' : ''; const volumesFlag = args.volumes ? '-v' : ''; return executeDockerCommand(`docker rm ${forceFlag} ${volumesFlag} ${containers}`.trim(), args.cwd); } export async function dockerRmi(args: z.infer<typeof dockerRmiSchema>): Promise<ToolResponse> { const images = Array.isArray(args.images) ? args.images.join(' ') : args.images; const forceFlag = args.force ? '-f' : ''; return executeDockerCommand(`docker rmi ${forceFlag} ${images}`.trim(), args.cwd); } // ========== TOOL DEFINITIONS FOR MCP ========== export const dockerTools = [ { name: 'docker_ps', description: 'List Docker containers with their status', inputSchema: { type: 'object', properties: { all: { type: 'boolean', default: false, description: 'Show all containers (default shows just running)' }, filter: { type: 'string', description: 'Filter output based on conditions (e.g., "status=running")' }, format: { type: 'string', enum: ['table', 'json'], default: 'table', description: 'Output format' }, cwd: { type: 'string', description: 'Working directory' } } } }, { name: 'docker_logs', description: 'Fetch logs from a container', inputSchema: { type: 'object', properties: { container: { type: 'string', description: 'Container name or ID' }, tail: { type: 'number', description: 'Number of lines to show from the end of logs' }, follow: { type: 'boolean', default: false, description: 'Follow log output (not recommended for MCP)' }, since: { type: 'string', description: 'Show logs since timestamp (e.g., "2023-01-01T00:00:00")' }, timestamps: { type: 'boolean', default: false, description: 'Show timestamps' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['container'] } }, { name: 'docker_exec', description: 'Execute a command inside a running container', inputSchema: { type: 'object', properties: { container: { type: 'string', description: 'Container name or ID' }, command: { type: 'string', description: 'Command to execute' }, workdir: { type: 'string', description: 'Working directory inside container' }, user: { type: 'string', description: 'User to run command as' }, env: { type: 'object', additionalProperties: { type: 'string' }, description: 'Environment variables' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['container', 'command'] } }, { name: 'docker_start', description: 'Start one or more stopped containers', inputSchema: { type: 'object', properties: { containers: { oneOf: [ { type: 'string' }, { type: 'array', items: { type: 'string' } } ], description: 'Container name(s) or ID(s)' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['containers'] } }, { name: 'docker_stop', description: 'Stop one or more running containers', inputSchema: { type: 'object', properties: { containers: { oneOf: [ { type: 'string' }, { type: 'array', items: { type: 'string' } } ], description: 'Container name(s) or ID(s)' }, timeout: { type: 'number', description: 'Seconds to wait before killing container' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['containers'] } }, { name: 'docker_restart', description: 'Restart one or more containers', inputSchema: { type: 'object', properties: { containers: { oneOf: [ { type: 'string' }, { type: 'array', items: { type: 'string' } } ], description: 'Container name(s) or ID(s)' }, timeout: { type: 'number', description: 'Seconds to wait before killing container' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['containers'] } }, { name: 'docker_inspect', description: 'Return low-level information on Docker objects (containers, images, networks, volumes)', inputSchema: { type: 'object', properties: { target: { type: 'string', description: 'Container, image, network, or volume name/ID' }, type: { type: 'string', enum: ['container', 'image', 'network', 'volume'], description: 'Type of object to inspect' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['target'] } }, { name: 'docker_build', description: 'Build a Docker image from a Dockerfile', inputSchema: { type: 'object', properties: { path: { type: 'string', description: 'Build context path (directory containing Dockerfile)' }, tag: { type: 'string', description: 'Name and optionally a tag (format: "name:tag")' }, dockerfile: { type: 'string', description: 'Name of Dockerfile (default: Dockerfile)' }, buildArgs: { type: 'object', additionalProperties: { type: 'string' }, description: 'Build-time variables' }, target: { type: 'string', description: 'Set target build stage' }, noCache: { type: 'boolean', default: false, description: 'Do not use cache when building' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['path'] } }, { name: 'docker_images', description: 'List Docker images', inputSchema: { type: 'object', properties: { filter: { type: 'string', description: 'Filter images (e.g., "reference=node:*")' }, all: { type: 'boolean', default: false, description: 'Show all images (default hides intermediate)' }, format: { type: 'string', enum: ['table', 'json'], default: 'table', description: 'Output format' }, cwd: { type: 'string', description: 'Working directory' } } } }, { name: 'docker_pull', description: 'Pull an image or repository from a registry', inputSchema: { type: 'object', properties: { image: { type: 'string', description: 'Image name and tag (e.g., "nginx:latest")' }, allTags: { type: 'boolean', default: false, description: 'Download all tagged images' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['image'] } }, { name: 'docker_compose_up', description: 'Create and start containers defined in docker-compose.yml', inputSchema: { type: 'object', properties: { detach: { type: 'boolean', default: true, description: 'Detached mode: run in background' }, build: { type: 'boolean', default: false, description: 'Build images before starting' }, services: { type: 'array', items: { type: 'string' }, description: 'Only start specific services' }, file: { type: 'string', description: 'Path to compose file (default: docker-compose.yml)' }, cwd: { type: 'string', description: 'Working directory (where docker-compose.yml is located)' } } } }, { name: 'docker_compose_down', description: 'Stop and remove containers, networks created by docker-compose up', inputSchema: { type: 'object', properties: { volumes: { type: 'boolean', default: false, description: 'Remove named volumes' }, removeOrphans: { type: 'boolean', default: false, description: 'Remove containers for services not in compose file' }, file: { type: 'string', description: 'Path to compose file' }, cwd: { type: 'string', description: 'Working directory' } } } }, { name: 'docker_compose_ps', description: 'List containers in a docker-compose stack', inputSchema: { type: 'object', properties: { services: { type: 'array', items: { type: 'string' }, description: 'Only show specific services' }, all: { type: 'boolean', default: false, description: 'Show all stopped containers' }, file: { type: 'string', description: 'Path to compose file' }, cwd: { type: 'string', description: 'Working directory' } } } }, { name: 'docker_compose_logs', description: 'View output from containers in a docker-compose stack', inputSchema: { type: 'object', properties: { services: { type: 'array', items: { type: 'string' }, description: 'Only show logs for specific services' }, tail: { type: 'number', description: 'Number of lines to show from end of logs' }, since: { type: 'string', description: 'Show logs since timestamp' }, timestamps: { type: 'boolean', default: false, description: 'Show timestamps' }, file: { type: 'string', description: 'Path to compose file' }, cwd: { type: 'string', description: 'Working directory' } } } }, { name: 'docker_rm', description: 'Remove one or more containers', inputSchema: { type: 'object', properties: { containers: { oneOf: [ { type: 'string' }, { type: 'array', items: { type: 'string' } } ], description: 'Container name(s) or ID(s) to remove' }, force: { type: 'boolean', default: false, description: 'Force removal of running containers' }, volumes: { type: 'boolean', default: false, description: 'Remove anonymous volumes' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['containers'] } }, { name: 'docker_rmi', description: 'Remove one or more images', inputSchema: { type: 'object', properties: { images: { oneOf: [ { type: 'string' }, { type: 'array', items: { type: 'string' } } ], description: 'Image name(s) or ID(s) to remove' }, force: { type: 'boolean', default: false, description: 'Force removal' }, cwd: { type: 'string', description: 'Working directory' } }, required: ['images'] } } ];

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/ConnorBoetig-dev/mcp2'

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