Skip to content

Commit a047060

Browse files
konardclaude
andcommitted
fix(mcp): add retry logic for Playwright MCP browser sidecar startup
Add retry mechanism to docker-git-playwright-mcp wrapper to handle browser sidecar startup delays. When Claude Code initializes the MCP server, the browser container may not be ready yet, causing the server to fail with connection errors. - Add configurable retry attempts (MCP_PLAYWRIGHT_RETRY_ATTEMPTS, default: 10) - Add configurable retry delay (MCP_PLAYWRIGHT_RETRY_DELAY, default: 2s) - Extract CDP fetch into a function for cleaner retry loop - Log retry progress to stderr for debugging - Update usage documentation with new environment variables - Add tests verifying retry logic in Dockerfile template Fixes #123 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ba2753d commit a047060

3 files changed

Lines changed: 43 additions & 1 deletion

File tree

packages/app/src/docker-git/cli/usage.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ Container runtime env (set via .orch/env/project.env):
8181
DOCKER_GIT_ZSH_AUTOSUGGEST_STRATEGY=... Suggestion sources (default: history completion)
8282
MCP_PLAYWRIGHT_ISOLATED=1|0 Isolated browser contexts (recommended for many Codex; default: 1)
8383
MCP_PLAYWRIGHT_CDP_ENDPOINT=http://... Override CDP endpoint (default: http://dg-<repo>-browser:9223)
84+
MCP_PLAYWRIGHT_RETRY_ATTEMPTS=<n> Retry attempts for browser sidecar startup wait (default: 10)
85+
MCP_PLAYWRIGHT_RETRY_DELAY=<seconds> Delay between retry attempts (default: 2)
8486
8587
Auth providers:
8688
github, gh GitHub CLI auth (tokens saved to env file)

packages/lib/src/core/templates/dockerfile.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,39 @@ if [[ -z "$CDP_ENDPOINT" ]]; then
105105
CDP_ENDPOINT="http://__SERVICE_NAME__-browser:9223"
106106
fi
107107
108+
# CHANGE: add retry logic for browser sidecar startup wait
109+
# WHY: the browser container may take time to initialize, causing MCP server to fail on first attempt
110+
# QUOTE(issue-123): "Почему MCP сервер лежит с ошибкой?"
111+
# REF: issue-123
112+
# SOURCE: n/a
113+
# FORMAT THEOREM: forall t in [1..max_attempts]: retry(t) -> eventually(cdp_ready) OR timeout_error
114+
# PURITY: SHELL
115+
# INVARIANT: script exits only after cdp_ready OR all retries exhausted
116+
# COMPLEXITY: O(max_attempts * timeout_per_attempt)
117+
MCP_PLAYWRIGHT_RETRY_ATTEMPTS="\${MCP_PLAYWRIGHT_RETRY_ATTEMPTS:-10}"
118+
MCP_PLAYWRIGHT_RETRY_DELAY="\${MCP_PLAYWRIGHT_RETRY_DELAY:-2}"
119+
120+
fetch_cdp_version() {
121+
curl -sSf --connect-timeout 3 --max-time 10 -H 'Host: 127.0.0.1:9222' "\${CDP_ENDPOINT%/}/json/version" 2>/dev/null
122+
}
123+
124+
JSON=""
125+
for attempt in $(seq 1 "$MCP_PLAYWRIGHT_RETRY_ATTEMPTS"); do
126+
if JSON="$(fetch_cdp_version)"; then
127+
break
128+
fi
129+
if [[ "$attempt" -lt "$MCP_PLAYWRIGHT_RETRY_ATTEMPTS" ]]; then
130+
echo "docker-git-playwright-mcp: waiting for browser sidecar (attempt $attempt/$MCP_PLAYWRIGHT_RETRY_ATTEMPTS)..." >&2
131+
sleep "$MCP_PLAYWRIGHT_RETRY_DELAY"
132+
fi
133+
done
134+
135+
if [[ -z "$JSON" ]]; then
136+
echo "docker-git-playwright-mcp: failed to connect to CDP endpoint $CDP_ENDPOINT after $MCP_PLAYWRIGHT_RETRY_ATTEMPTS attempts" >&2
137+
exit 1
138+
fi
139+
108140
# kechangdev/browser-vnc binds Chromium CDP on 127.0.0.1:9222; it also host-checks HTTP requests.
109-
JSON="$(curl -sSf --connect-timeout 3 --max-time 10 -H 'Host: 127.0.0.1:9222' "\${CDP_ENDPOINT%/}/json/version")"
110141
WS_URL="$(printf "%s" "$JSON" | node -e 'const fs=require("fs"); const j=JSON.parse(fs.readFileSync(0,"utf8")); process.stdout.write(j.webSocketDebuggerUrl || "")')"
111142
if [[ -z "$WS_URL" ]]; then
112143
echo "docker-git-playwright-mcp: webSocketDebuggerUrl missing" >&2

packages/lib/tests/usecases/mcp-playwright.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ describe("enableMcpPlaywrightProjectFiles", () => {
120120
const dockerfileAfter = yield* _(fs.readFileString(path.join(outDir, "Dockerfile")))
121121
expect(dockerfileAfter).toContain("@playwright/mcp")
122122

123+
// CHANGE: verify retry logic is included in docker-git-playwright-mcp wrapper
124+
// WHY: issue-123 requires retry mechanism to handle browser sidecar startup delays
125+
// QUOTE(issue-123): "Почему MCP сервер лежит с ошибкой?"
126+
// REF: issue-123
127+
expect(dockerfileAfter).toContain("MCP_PLAYWRIGHT_RETRY_ATTEMPTS")
128+
expect(dockerfileAfter).toContain("MCP_PLAYWRIGHT_RETRY_DELAY")
129+
expect(dockerfileAfter).toContain("fetch_cdp_version()")
130+
expect(dockerfileAfter).toContain("waiting for browser sidecar")
131+
123132
const browserDockerfileExists = yield* _(fs.exists(path.join(outDir, "Dockerfile.browser")))
124133
const startExtraExists = yield* _(fs.exists(path.join(outDir, "mcp-playwright-start-extra.sh")))
125134
expect(browserDockerfileExists).toBe(true)

0 commit comments

Comments
 (0)