Skip to content

fix(docker): resolve docker by absolute path on macOS spawn; honest docker_available (#696)#697

Merged
Dumbris merged 2 commits into
mainfrom
fix/mcp-2715-docker-spawn-path
Jun 16, 2026
Merged

fix(docker): resolve docker by absolute path on macOS spawn; honest docker_available (#696)#697
Dumbris merged 2 commits into
mainfrom
fix/mcp-2715-docker-spawn-path

Conversation

@Dumbris

@Dumbris Dumbris commented Jun 16, 2026

Copy link
Copy Markdown
Member

Problem (GitHub #696)

On macOS with Docker Desktop installed the default way (without the optional, admin-gated "install CLI tools" step), the docker CLI lives only at /Applications/Docker.app/Contents/Resources/bin/dockernot on any standard PATH dir. mcpproxy spawned Docker-isolated upstream servers with bare docker on a sanitized PATH that omits the bundle dir, so docker was unresolvable at spawn:

zsh:1: command not found: docker   (repeated ~20x, retry_count: 21)

…while upstream_servers list misleadingly reported docker_status.available: true / per-server docker_available: true.

Fixes (TDD — failing test first for each)

  1. Spawn by absolute path (root cause)internal/upstream/core/connection_docker.go: setupDockerIsolation resolves docker via shellwrap.ResolveDockerPath (mirroring newDockerCmd) and spawns the absolute path before shell-wrapping; falls back to bare docker only when resolution fails. Indirected via a package var so tests stub resolution with no real Docker install.
  2. Enhanced spawn PATH (defense in depth)internal/secureenv/manager.go: add /Applications/Docker.app/Contents/Resources/bin on darwin (existence-filtered). Candidate list split into a pure, unit-testable unixCandidatePaths().
  3. Honest docker_availableinternal/server/mcp.go (resolveDockerStatus) + internal/runtime/runtime.go (IsDockerAvailable) + internal/management/diagnostics.go: report available only when the CLI is resolvable and docker info succeeds — no bare-docker fallback that misreports. New docker_status.docker_path exposes the resolved binary; available:false with docker_path:"" means unresolvable.
  4. Actionable error + stderr dedupinternal/upstream/core/monitoring.go: formatRecentStderr leads a single actionable "Docker CLI not found" hint on command not found (zsh + bash forms) and collapses identical consecutive lines into … (repeated N×), replacing the 20-line wall.

Cross-platform

macOS-specific paths guarded behind runtime.GOOS=="darwin"; Linux/Windows unaffected. Shell-script fake-docker tests skip on Windows.

Docs (ENG-9)

docs/docker-isolation.md: new docker_path field, corrected telemetry-counter semantics, and a command not found: docker troubleshooting entry.

Verification

  • go build ./cmd/mcpproxy (personal) and go build -tags server ./cmd/mcpproxy — both pass.
  • New tests pass: internal/secureenv (bundle-path inclusion), internal/upstream/core (absolute-path spawn, cidfile insertion, bare fallback, hint extraction, dedup, wall→hint), internal/server (unavailable/working/daemon-down).
  • golangci-lint (CI v2 config) — 0 issues.
  • Full -race over touched packages running in CI.

Out of scope

  • Windows/Linux Docker Desktop bundle-path discovery (TODO).
  • MaxConnectionRetries unchanged.

Related #696

…ocker_available (#696)

On macOS, Docker Desktop installed the default way (without the optional,
admin-gated "install CLI tools" step) leaves the docker CLI only inside the
app bundle at /Applications/Docker.app/Contents/Resources/bin/docker, which is
not on any standard PATH dir. mcpproxy spawned isolated servers with bare
`docker` on a sanitized PATH that omits the bundle dir, so docker was
unresolvable at spawn (~20x `command not found: docker`), while
`upstream_servers list` misreported `docker_available: true`.

- Spawn (root cause): setupDockerIsolation resolves docker to an absolute path
  via shellwrap.ResolveDockerPath (mirroring newDockerCmd) before shell-wrapping;
  falls back to bare "docker" only when resolution fails.
- Enhanced spawn PATH (defense in depth): add the Docker Desktop bundle bin dir
  on darwin; split the candidate list into a pure, unit-testable function.
- Honest availability: MCPProxyServer.resolveDockerStatus and
  Runtime.IsDockerAvailable report available only when the CLI is resolvable AND
  `docker info` succeeds — no bare-docker fallback that would misreport. Surface
  the resolved binary as docker_status.docker_path; diagnostics.checkDockerDaemon
  reports unavailable with an actionable error when unresolvable.
- Diagnostics: formatRecentStderr leads a single actionable "Docker CLI not
  found" hint on `command not found` and collapses identical consecutive lines
  into "… (repeated N×)", replacing the 20-line wall.

macOS-specific paths guarded behind runtime.GOOS=="darwin"; Linux/Windows
unaffected. Docs updated for docker_path, telemetry counter semantics, and a
new troubleshooting entry.

Related #696
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 16, 2026

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 6fa8ffa
Status: ✅  Deploy successful!
Preview URL: https://ba4c1d5a.mcpproxy-docs.pages.dev
Branch Preview URL: https://fix-mcp-2715-docker-spawn-pa.mcpproxy-docs.pages.dev

View logs

@codecov-commenter

codecov-commenter commented Jun 16, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 78.26087% with 20 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/upstream/core/connection_docker.go 56.25% 5 Missing and 2 partials ⚠️
internal/management/diagnostics.go 0.00% 4 Missing ⚠️
internal/server/mcp.go 80.95% 4 Missing ⚠️
internal/runtime/runtime.go 62.50% 2 Missing and 1 partial ⚠️
internal/secureenv/manager.go 80.00% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

…n test

insertCidfileIntoShellDockerCommand checked shellArgs[n-2]=="-c" to
distinguish a shell-wrapped command string from an unrecognised format.
On Windows with cmd.exe (no bash), WrapWithUserShell returns ["/c", cmdStr]
so the flag is "/c", not "-c"; the guard rejected it and silently skipped
cidfile insertion, breaking TestSetupDockerIsolationCidfileInsertionWithAbsolutePath
on windows-latest CI.

Accept both flags ("-c" and "/c") so cidfile insertion works on every
platform. Add TestInsertCidfileWindowsCmdFormat (pure unit, no real Docker)
to pin the Windows cmd format.
@github-actions

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: fix/mcp-2715-docker-spawn-path

Available Artifacts

  • archive-darwin-amd64 (28 MB)
  • archive-darwin-arm64 (25 MB)
  • archive-linux-amd64 (16 MB)
  • archive-linux-arm64 (14 MB)
  • archive-windows-amd64 (28 MB)
  • archive-windows-arm64 (25 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (21 MB)
  • installer-dmg-darwin-arm64 (19 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 27625094138 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

@mcpproxy-gatekeeper mcpproxy-gatekeeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved via Claude Code review (Codex quota exhausted).

@Dumbris Dumbris merged commit 79849e0 into main Jun 16, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants