Skip to content

Commit daeff40

Browse files
authored
Merge pull request #126 from konard/issue-123-a5ba76ddd9e9
fix(mcp): add retry logic for Playwright MCP browser sidecar startup
2 parents 2b906df + 5c5b20b commit daeff40

File tree

3 files changed

+43
-1
lines changed

3 files changed

+43
-1
lines changed

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)