on main help sign in
fix(mcp/docker): only write config after docker startup succeeds
commit351e5620f7c63cec6c2d2b42ce4b5e17031f6423 authorChristian Rocha <christian@rocha.is> dateThu 19 Mar 2026, 11:37 (7 months ago) parent6c61cae
2 files changed+35−11
internal/config/docker_mcp.go+23−9 internal/ui/model/ui.go+12−2
internal/config/docker_mcp.go+23−9
@@ -34,30 +34,44 @@ func (c *Config) IsDockerMCPEnabled() bool {
3434 return exists
3535}
3636
37−// EnableDockerMCP adds Docker MCP configuration and persists it.
38−func (s *ConfigStore) EnableDockerMCP() error {
39− if !IsDockerMCPAvailable() {
40− return fmt.Errorf("docker mcp is not available, please ensure docker is installed and 'docker mcp version' succeeds")
41− }
42−
43− mcpConfig := MCPConfig{
37+func DockerMCPConfig() MCPConfig {
38+ return MCPConfig{
4439 Type: MCPStdio,
4540 Command: "docker",
4641 Args: []string{"mcp", "gateway", "run"},
4742 Disabled: false,
4843 }
44+}
45+
46+func (s *ConfigStore) PrepareDockerMCPConfig() (MCPConfig, error) {
47+ if !IsDockerMCPAvailable() {
48+ return MCPConfig{}, fmt.Errorf("docker mcp is not available, please ensure docker is installed and 'docker mcp version' succeeds")
49+ }
4950
50− // Add to in-memory config.
51+ mcpConfig := DockerMCPConfig()
5152 if s.config.MCP == nil {
5253 s.config.MCP = make(map[string]MCPConfig)
5354 }
5455 s.config.MCP[DockerMCPName] = mcpConfig
56+ return mcpConfig, nil
57+}
5558
56− // Persist to config file.
59+func (s *ConfigStore) PersistDockerMCPConfig(mcpConfig MCPConfig) error {
5760 if err := s.SetConfigField(ScopeGlobal, "mcp."+DockerMCPName, mcpConfig); err != nil {
5861 return fmt.Errorf("failed to persist docker mcp configuration: %w", err)
5962 }
63+ return nil
64+}
6065
66+// EnableDockerMCP adds Docker MCP configuration and persists it.
67+func (s *ConfigStore) EnableDockerMCP() error {
68+ mcpConfig, err := s.PrepareDockerMCPConfig()
69+ if err != nil {
70+ return err
71+ }
72+ if err := s.PersistDockerMCPConfig(mcpConfig); err != nil {
73+ return err
74+ }
6175 return nil
6276}
6377
internal/ui/model/ui.go+12−2
@@ -3465,16 +3465,26 @@ func (m *UI) copyChatHighlight() tea.Cmd {
34653465
34663466func (m *UI) enableDockerMCP() tea.Msg {
34673467 store := m.com.Store()
3468− if err := store.EnableDockerMCP(); err != nil {
3468+ // Stage Docker MCP in memory first so startup and persistence can be atomic.
3469+ mcpConfig, err := store.PrepareDockerMCPConfig()
3470+ if err != nil {
34693471 return util.ReportError(err)()
34703472 }
34713473
3472− // Initialize the Docker MCP client immediately.
34733474 ctx := context.Background()
34743475 if err := mcp.InitializeSingle(ctx, config.DockerMCPName, store); err != nil {
3476+ // Roll back in-memory state when startup fails.
3477+ delete(store.Config().MCP, config.DockerMCPName)
34753478 return util.ReportError(fmt.Errorf("docker MCP enabled but failed to start: %w", err))()
34763479 }
34773480
3481+ if err := store.PersistDockerMCPConfig(mcpConfig); err != nil {
3482+ // Roll back runtime and in-memory state if persistence fails.
3483+ _ = mcp.DisableSingle(store, config.DockerMCPName)
3484+ delete(store.Config().MCP, config.DockerMCPName)
3485+ return util.ReportError(fmt.Errorf("docker MCP started but failed to persist configuration: %w", err))()
3486+ }
3487+
34783488 return util.NewInfoMsg("Docker MCP enabled and started successfully")
34793489}
34803490