docker_mcp.go

  1package config
  2
  3import (
  4	"context"
  5	"fmt"
  6	"os/exec"
  7	"sync"
  8	"time"
  9)
 10
 11var dockerMCPVersionRunner = func(ctx context.Context) error {
 12	cmd := exec.CommandContext(ctx, "docker", "mcp", "version")
 13	return cmd.Run()
 14}
 15
 16const dockerMCPAvailabilityTTL = 10 * time.Second
 17
 18var dockerMCPAvailabilityCache struct {
 19	mu        sync.Mutex
 20	available bool
 21	checkedAt time.Time
 22	known     bool
 23}
 24
 25// DockerMCPName is the name of the Docker MCP configuration.
 26const DockerMCPName = "docker"
 27
 28// IsDockerMCPAvailable checks if Docker MCP is available by running
 29// 'docker mcp version'.
 30func IsDockerMCPAvailable() bool {
 31	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
 32	defer cancel()
 33
 34	err := dockerMCPVersionRunner(ctx)
 35	return err == nil
 36}
 37
 38// DockerMCPAvailabilityCached returns the cached Docker MCP availability and
 39// whether the cached value is still fresh.
 40func DockerMCPAvailabilityCached() (available bool, known bool) {
 41	dockerMCPAvailabilityCache.mu.Lock()
 42	defer dockerMCPAvailabilityCache.mu.Unlock()
 43
 44	if !dockerMCPAvailabilityCache.known {
 45		return false, false
 46	}
 47	if time.Since(dockerMCPAvailabilityCache.checkedAt) > dockerMCPAvailabilityTTL {
 48		return dockerMCPAvailabilityCache.available, false
 49	}
 50	return dockerMCPAvailabilityCache.available, true
 51}
 52
 53// RefreshDockerMCPAvailability refreshes and caches Docker MCP availability.
 54func RefreshDockerMCPAvailability() bool {
 55	available := IsDockerMCPAvailable()
 56	dockerMCPAvailabilityCache.mu.Lock()
 57	dockerMCPAvailabilityCache.available = available
 58	dockerMCPAvailabilityCache.checkedAt = time.Now()
 59	dockerMCPAvailabilityCache.known = true
 60	dockerMCPAvailabilityCache.mu.Unlock()
 61	return available
 62}
 63
 64// IsDockerMCPEnabled checks if Docker MCP is already configured.
 65func (c *Config) IsDockerMCPEnabled() bool {
 66	if c.MCP == nil {
 67		return false
 68	}
 69	_, exists := c.MCP[DockerMCPName]
 70	return exists
 71}
 72
 73// DockerMCPConfig returns the default Docker MCP stdio configuration.
 74func DockerMCPConfig() MCPConfig {
 75	return MCPConfig{
 76		Type:     MCPStdio,
 77		Command:  "docker",
 78		Args:     []string{"mcp", "gateway", "run"},
 79		Disabled: false,
 80	}
 81}
 82
 83// PrepareDockerMCPConfig validates Docker MCP availability and stages the
 84// Docker MCP configuration in memory.
 85func (s *ConfigStore) PrepareDockerMCPConfig() (MCPConfig, error) {
 86	if !IsDockerMCPAvailable() {
 87		return MCPConfig{}, fmt.Errorf("docker mcp is not available, please ensure docker is installed and 'docker mcp version' succeeds")
 88	}
 89
 90	mcpConfig := DockerMCPConfig()
 91	if s.config.MCP == nil {
 92		s.config.MCP = make(map[string]MCPConfig)
 93	}
 94	s.config.MCP[DockerMCPName] = mcpConfig
 95	return mcpConfig, nil
 96}
 97
 98// PersistDockerMCPConfig persists a previously prepared Docker MCP
 99// configuration to the global config file.
100func (s *ConfigStore) PersistDockerMCPConfig(mcpConfig MCPConfig) error {
101	if err := s.SetConfigField(ScopeGlobal, "mcp."+DockerMCPName, mcpConfig); err != nil {
102		return fmt.Errorf("failed to persist docker mcp configuration: %w", err)
103	}
104	return nil
105}
106
107// EnableDockerMCP adds Docker MCP configuration and persists it.
108func (s *ConfigStore) EnableDockerMCP() error {
109	mcpConfig, err := s.PrepareDockerMCPConfig()
110	if err != nil {
111		return err
112	}
113	if err := s.PersistDockerMCPConfig(mcpConfig); err != nil {
114		return err
115	}
116	return nil
117}
118
119// DisableDockerMCP removes Docker MCP configuration and persists the change.
120func (s *ConfigStore) DisableDockerMCP() error {
121	if s.config.MCP == nil {
122		return nil
123	}
124
125	// Remove from in-memory config.
126	delete(s.config.MCP, DockerMCPName)
127
128	// Persist the updated MCP map to the config file.
129	if err := s.SetConfigField(ScopeGlobal, "mcp", s.config.MCP); err != nil {
130		return fmt.Errorf("failed to persist docker mcp removal: %w", err)
131	}
132
133	return nil
134}