77 lines
3.7 KiB
YAML
77 lines
3.7 KiB
YAML
#ddev-generated
|
|
# Opt-in headless browser for Claude Code: Playwright MCP with headless Chromium.
|
|
#
|
|
# This is the template. It does nothing where it is: "ddev claude-browser enable"
|
|
# copies it to .ddev/docker-compose.claude-browser.yaml and restarts the project, and
|
|
# install.sh registers it with Claude Code as the "playwright" MCP server
|
|
# (http://claude-browser:8931/mcp). While the copy keeps the #ddev-generated line,
|
|
# install.sh refreshes it from this template after add-on upgrades; remove that line
|
|
# from the copy to customise it.
|
|
#
|
|
# Pick another release with CLAUDE_BROWSER_IMAGE in .ddev/.env, e.g.
|
|
# CLAUDE_BROWSER_IMAGE=mcr.microsoft.com/playwright/mcp:v0.0.84
|
|
services:
|
|
claude-browser:
|
|
image: ${CLAUDE_BROWSER_IMAGE:-mcr.microsoft.com/playwright/mcp:v0.0.83}-${DDEV_SITENAME}-built
|
|
build:
|
|
# The upstream image runs as "node" (uid 1000). Turn that user into the host user
|
|
# (same name, uid and gid) so screenshots and other files the browser writes into
|
|
# the mounted project belong to you. Same approach as ddev-selenium-standalone-chrome.
|
|
dockerfile_inline: |
|
|
ARG CLAUDE_BROWSER_IMAGE="scratch"
|
|
FROM $${CLAUDE_BROWSER_IMAGE}
|
|
USER root
|
|
ARG username
|
|
ARG uid
|
|
ARG gid
|
|
RUN set -eu; \
|
|
old_uid="$$(id -u node)"; old_gid="$$(id -g node)"; \
|
|
for u in "$$(getent passwd "$${uid}" | cut -d: -f1)" "$$(getent passwd "$${username}" | cut -d: -f1)"; do \
|
|
if [ -n "$${u}" ] && [ "$${u}" != "node" ]; then userdel "$${u}"; fi; \
|
|
done; \
|
|
for g in "$$(getent group "$${gid}" | cut -d: -f1)" "$$(getent group "$${username}" | cut -d: -f1)"; do \
|
|
if [ -n "$${g}" ] && [ "$${g}" != "node" ]; then groupdel "$${g}" || true; fi; \
|
|
done; \
|
|
groupmod -o -g "$${gid}" node; \
|
|
if ! getent group "$${username}" >/dev/null; then groupmod -n "$${username}" node; fi; \
|
|
usermod -o -u "$${uid}" -g "$${gid}" node; \
|
|
if [ "$${username}" != "node" ]; then usermod -l "$${username}" node; fi; \
|
|
find / -xdev -uid "$${old_uid}" -exec chown -h "$${uid}" {} +; \
|
|
find / -xdev -gid "$${old_gid}" -exec chgrp -h "$${gid}" {} +
|
|
USER $${uid}:$${gid}
|
|
args:
|
|
CLAUDE_BROWSER_IMAGE: ${CLAUDE_BROWSER_IMAGE:-mcr.microsoft.com/playwright/mcp:v0.0.83}
|
|
username: ${DDEV_USER}
|
|
uid: ${DDEV_UID}
|
|
gid: ${DDEV_GID}
|
|
container_name: ddev-${DDEV_SITENAME}-claude-browser
|
|
user: "${DDEV_UID}:${DDEV_GID}"
|
|
restart: "no"
|
|
# Chromium needs more than Docker's 64 MB default /dev/shm for heavier pages.
|
|
shm_size: 1gb
|
|
# Appended to the image's entrypoint (node cli.js --headless --browser chromium --no-sandbox).
|
|
command:
|
|
- --port=8931
|
|
- --host=0.0.0.0
|
|
# The port is only exposed on the project's Docker network, never published to
|
|
# the host, so the Host-header check adds nothing here.
|
|
- --allowed-hosts=*
|
|
# DDEV's mkcert CA is not in this container's trust store.
|
|
- --ignore-https-errors
|
|
# Fresh in-memory profile per session, so parallel "ddev claude" sessions never
|
|
# fight over one profile directory.
|
|
- --isolated
|
|
# Same path as in the web container, so paths in tool results work for Claude as-is.
|
|
- --output-dir=/var/www/html/.ddev/claude-code/browser-output
|
|
- --file-paths=absolute
|
|
expose:
|
|
- 8931
|
|
volumes:
|
|
# The project at the web container's path: file uploads and saved screenshots
|
|
# use paths Claude can read directly.
|
|
- "../:/var/www/html"
|
|
x-ddev:
|
|
describe-info: |
|
|
Playwright MCP: http://claude-browser:8931/mcp
|
|
Toggle with: ddev claude-browser enable|disable
|