UNPKG

docker-mcp

Version:

A Model Context Protocol (MCP) server that enables AI agents to interact with Docker containers locally or remotely via SSH. Provides comprehensive Docker management capabilities including container operations, logs, monitoring, and cleanup.

168 lines 6.09 kB
import { existsSync, readFileSync } from 'fs'; import { homedir } from 'os'; import { join } from 'path'; /** * Detects the appropriate Docker socket path based on the environment */ export function detectDockerSocketPath() { // Common Docker socket paths to check const socketPaths = [ // OrbStack socket join(homedir(), '.orbstack/run/docker.sock'), // Docker Desktop on macOS join(homedir(), '.docker/run/docker.sock'), // Default Unix socket '/var/run/docker.sock', // Docker Desktop on Windows (if running in WSL) '/mnt/wsl/docker-desktop/shared-sockets/guest-services/docker.sock' ]; for (const socketPath of socketPaths) { if (existsSync(socketPath)) { return socketPath; } } // If no socket found, return default and let Docker handle the error return '/var/run/docker.sock'; } /** * Parses SSH config to extract host details */ export function parseSSHConfig(hostname) { const sshConfigPath = join(homedir(), '.ssh/config'); if (!existsSync(sshConfigPath)) { return { host: hostname }; } try { const configContent = readFileSync(sshConfigPath, 'utf-8'); const lines = configContent.split('\n'); let currentHost = ''; let hostConfig = {}; let isTargetHost = false; for (const line of lines) { const trimmed = line.trim(); if (trimmed.startsWith('Host ')) { // Save previous host config if it was our target if (isTargetHost) { break; } currentHost = trimmed.replace('Host ', '').trim(); isTargetHost = currentHost === hostname; hostConfig = { host: hostname }; } else if (isTargetHost && trimmed.includes(' ')) { const [key, ...valueParts] = trimmed.split(' '); const value = valueParts.join(' ').trim(); switch (key.toLowerCase()) { case 'hostname': hostConfig.host = value; break; case 'port': hostConfig.port = parseInt(value, 10); break; case 'user': hostConfig.username = value; break; case 'identityfile': // Handle tilde expansion and quotes const keyPath = value.replace(/^["']|["']$/g, '').replace('~', homedir()); hostConfig.privateKey = keyPath; break; } } } return isTargetHost ? hostConfig : { host: hostname }; } catch (error) { console.error(`Failed to parse SSH config: ${error}`); return { host: hostname }; } } /** * Creates Docker options for remote connection */ export function createRemoteDockerOptions(config) { const sshConfig = parseSSHConfig(config.host); // Merge SSH config with provided config (provided config takes precedence) const mergedConfig = { ...sshConfig, ...config }; // Read private key if file path is provided let privateKeyContent = mergedConfig.privateKeyContent; if (!privateKeyContent && mergedConfig.privateKey) { try { privateKeyContent = readFileSync(mergedConfig.privateKey, 'utf-8'); } catch (error) { console.error(`Failed to read private key from ${mergedConfig.privateKey}:`, error); } } console.error('SSH configuration for Docker connection:', { host: mergedConfig.host, port: mergedConfig.port || 22, username: mergedConfig.username || 'root', socketPath: mergedConfig.socketPath || '/var/run/docker.sock', hasPrivateKey: !!privateKeyContent }); console.error('Note: docker-modem SSH support appears to have issues.'); console.error('Consider using manual SSH tunneling instead:'); console.error(`ssh -L 2375:/var/run/docker.sock -i ${mergedConfig.privateKey} ${mergedConfig.username}@${mergedConfig.host}`); console.error('Then set DOCKER_HOST=tcp://localhost:2375'); // For now, fall back to using SSH protocol with docker-modem const dockerOptions = { protocol: 'ssh', host: mergedConfig.host, port: mergedConfig.port || 22, username: mergedConfig.username || 'root', socketPath: mergedConfig.socketPath || '/var/run/docker.sock', timeout: mergedConfig.timeout || 10000 }; // Add SSH options if private key is available if (privateKeyContent) { dockerOptions.sshOptions = { privateKey: privateKeyContent, passphrase: mergedConfig.passphrase }; } console.error('Docker options with SSH protocol:', dockerOptions); return dockerOptions; } /** * Creates Docker options with the best available configuration */ export function createDockerOptions(config) { // If explicit Docker options are provided, use them if (config?.dockerOptions) { return config.dockerOptions; } // If remote configuration is provided, use it if (config?.remote) { return createRemoteDockerOptions(config.remote); } // Default to local Docker daemon const socketPath = detectDockerSocketPath(); return { socketPath, timeout: 10000 }; } /** * Parses connection string in format: [user@]host[:port] */ export function parseConnectionString(connectionString) { const parts = connectionString.split('@'); let username; let hostPart; if (parts.length === 2) { username = parts[0]; hostPart = parts[1]; } else { hostPart = parts[0]; } const [host, portString] = hostPart.split(':'); const port = portString ? parseInt(portString, 10) : undefined; return { host, port, username }; } //# sourceMappingURL=docker-config.js.map