Skip to content

fix(upstream): prepend docker bundle dir to child PATH so docker finds its credential helper (#715)#716

Merged
Dumbris merged 1 commit into
mainfrom
fix/715-docker-bundle-dir-path
Jun 18, 2026
Merged

fix(upstream): prepend docker bundle dir to child PATH so docker finds its credential helper (#715)#716
Dumbris merged 1 commit into
mainfrom
fix/715-docker-bundle-dir-path

Conversation

@Dumbris

@Dumbris Dumbris commented Jun 18, 2026

Copy link
Copy Markdown
Member

Summary

Follow-on to #696 (shipped v0.41.1). Fixes GitHub issue #715 / MCP-2877.

We now invoke the absolute docker path for docker-isolated servers, but the spawned docker process inherits mcpproxy's sanitized PATH, which omits the Docker Desktop bundle dir. So docker cannot exec the sibling binaries it shells out to — most importantly the credential helper from ~/.docker/config.json. Any docker-isolated server whose isolation image isn't already cached fails to pull:

Unable to find image 'ghcr.io/astral-sh/uv:python3.11-bookworm' locally
docker: error getting credentials - err: exec: "docker-credential-desktop": executable file not found in $PATH

Docker Desktop's default credsStore: desktop makes docker run docker-credential-desktop for every registry op (even anonymous public pulls). That helper lives in the same bundle dir as the docker CLI (/Applications/Docker.app/Contents/Resources/bin/), which mcpproxy's spawn PATH omits. A pre-pulled image sidesteps it (no registry op), which is why #696 looked complete.

Fix

  • resolveDockerSpawn / setupDockerIsolation now also return the resolved docker binary's bundle directory (filepath.Dir(resolved)), set whenever docker resolves to a verified absolute executable — on both the direct-exec and login-shell-wrap fallback paths.
  • New pure helper prependDockerDirToPath(env, dir): prepends the bundle dir to the PATH entry (existing entries preserved), dedup-aware (no-op if already present), uses os.PathListSeparator (cross-platform), never mutates the input, and is a no-op for an empty dir.
  • Wired into all three docker spawn paths: isolated uvx/npx servers (connectStdio + launcher) and user-supplied docker run … upstreams.

This generalizes the #696 fix from "find docker" to "let docker find its own tooling" (docker-credential-*, docker-compose, docker-buildx).

Tests (TDD — failing test written first)

  • connection_docker_path_test.go: asserts the bundle dir is reported on direct-exec and on the shell-wrap fallback, not reported when docker is unresolved, and full unit coverage of prependDockerDirToPath (prepend/dedup/empty/no-PATH/empty-value/no-mutation).
  • go test ./internal/upstream/... -race
  • CI v2 linter (golangci-lint --config .github/.golangci.yml ./internal/upstream/...) → 0 issues ✅
  • ./scripts/test-api-e2e.sh → 65/65 passed ✅

Docs

  • docs/docker-isolation.md: new troubleshooting entry for the docker-credential-desktop … not found in $PATH symptom.

Lineage

#696 → MCP-2715 (find docker) → MCP-2744 (wire resolved path into spawn) → #714/MCP-2868 (shared resolveDockerSpawn) → this (let docker find its sibling tooling).

Related #715

… helper

Docker-isolated (and user-supplied `docker run`) upstreams direct-exec the
absolute docker binary (#696), but the spawned docker inherits mcpproxy's
sanitized PATH, which omits the Docker Desktop bundle dir. So docker cannot
exec the sibling binaries it shells out to — most importantly the credential
helper from ~/.docker/config.json (`credsStore: desktop` →
docker-credential-desktop). Any docker-isolated server whose isolation image
isn't already cached fails to pull with:

  error getting credentials - err: exec: "docker-credential-desktop":
  executable file not found in $PATH

resolveDockerSpawn / setupDockerIsolation now also return the resolved docker
binary's bundle directory, and both stdio spawn paths plus the launcher prepend
it to the child PATH via prependDockerDirToPath (dedup-aware, os.PathListSeparator,
no-op when docker did not resolve to an absolute path). This generalizes the
#696 fix from "find docker" to "let docker find its own tooling".

Related #715
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 1e6d619
Status: ✅  Deploy successful!
Preview URL: https://e7cef8cb.mcpproxy-docs.pages.dev
Branch Preview URL: https://fix-715-docker-bundle-dir-pa.mcpproxy-docs.pages.dev

View logs

@codecov-commenter

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 72.22222% with 10 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/upstream/core/connection_stdio.go 0.00% 6 Missing ⚠️
internal/upstream/core/connection_launcher.go 0.00% 3 Missing ⚠️
internal/upstream/core/connection_docker.go 96.29% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

Copy link
Copy Markdown

📦 Build Artifacts

Workflow Run: View Run
Branch: fix/715-docker-bundle-dir-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 27754880787 --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 reflecting an independent Claude Code review on a checkout of the PR branch (HEAD 1e6d619). Verified: prependDockerDirToPath prepends the resolved docker bundle dir to the spawn env PATH on all covered paths (isolation uvx/npx + user-supplied docker run in stdio + launcher isolation), is a correct no-op when docker is not an absolute path, dedups, does not mutate input, and uses os.PathListSeparator. Targeted tests pass under -race; CI fully green. Two non-blocking findings filed as follow-up (launcher user-supplied 'docker run' else-branch is pre-existing tech debt, not a regression; Windows PATH casing matches existing secureenv convention).

@Dumbris Dumbris merged commit 6998910 into main Jun 18, 2026
37 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