From 055dd88f5ebf7d17abb616b0f607e7f96ce0c695 Mon Sep 17 00:00:00 2001 From: Raphael Westphal Date: Thu, 20 Aug 2026 10:43:23 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20MCP=20in=20containers=20=E2=80=94=20clo?= =?UTF-8?q?akbrowser-mcp=20+=20chromium=20libs=20baked,=20mcp.json=20stand?= =?UTF-8?q?ard=20config,=20shared=20host=20chromium=20cache=20mounted=20ro?= =?UTF-8?q?;=20playwright=20via=20npx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- daemon/docker.go | 7 +++++++ docker-compose.yml | 1 + docker/control.Dockerfile | 8 ++++++++ docker/mcp.json | 13 +++++++++++++ docker/worker.Dockerfile | 11 +++++++++++ 5 files changed, 40 insertions(+) create mode 100644 docker/mcp.json diff --git a/daemon/docker.go b/daemon/docker.go index 37c707b..094472b 100644 --- a/daemon/docker.go +++ b/daemon/docker.go @@ -49,6 +49,7 @@ const ( authMountTarget string = "/root/.pi/agent/auth.json" envHostPiAgentDir string = "LVMH_HOST_PI_AGENT_DIR" envSecretsDir string = "LVMH_SECRETS_DIR" + envCloakCacheDir string = "LVMH_CLOAK_CACHE_DIR" sshMountTarget string = "/root/.ssh" gitconfigMountTarget string = "/root/.gitconfig" workspaceMount string = "/workspace" @@ -555,6 +556,12 @@ func (s *Spawner) createAndStart(ctx context.Context, repo, slug, sessionID stri if hostAgent := os.Getenv(envHostPiAgentDir); hostAgent != "" { binds = append(binds, hostAgent+"/auth.json:"+authMountTarget+":ro") } + // Shared cloakbrowser chromium cache (host path, read-only): browsers + // are ~700MB; one copy serves every container. CLOAKBROWSER_CACHE_DIR + // points the MCP at it (see docker/mcp.json). + if cb := os.Getenv(envCloakCacheDir); cb != "" { + binds = append(binds, cb+":/cloakbrowser-cache:ro") + } // Deploy secrets (ssh key + known_hosts + config, gitconfig), read-only. // Bind sources resolve on the HOST (docker.sock semantics), so this env // must carry the host path of the secrets dir. diff --git a/docker-compose.yml b/docker-compose.yml index 68237a5..373b287 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,6 +20,7 @@ services: # this must be the HOST path of the pi config (auth.json etc.). LVMH_HOST_PI_AGENT_DIR: ${LVMH_PI_AGENT_DIR:-/home/alarm/.dotfiles/pi/agent} LVMH_SECRETS_DIR: /zdata/root/lvmh-secrets + LVMH_CLOAK_CACHE_DIR: /home/alarm/.cloakbrowser OPENAI_API_KEY: ${OPENAI_API_KEY:-} GEMINI_API_KEY: ${GEMINI_API_KEY:-} DEEPSEEK_KEY: ${DEEPSEEK_KEY:-} diff --git a/docker/control.Dockerfile b/docker/control.Dockerfile index 4a27744..d356634 100644 --- a/docker/control.Dockerfile +++ b/docker/control.Dockerfile @@ -8,6 +8,9 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ bash ca-certificates git ripgrep curl xz-utils make jq golang-go \ docker.io \ + libglib2.0-0 libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 \ + libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \ + libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 \ && rm -rf /var/lib/apt/lists/* RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent @@ -34,6 +37,11 @@ COPY docker/bridge /bridge COPY docker/ops-context/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh +# MCP tooling (same as workers; ops also browses). +RUN npm-real install -g --ignore-scripts cloakbrowser-mcp@1.4.0 \ + && mkdir -p /root/.config/mcp +COPY docker/mcp.json /root/.config/mcp/mcp.json + ENV PI_SESSION_DIR=/pi-sessions WORKDIR /ops ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/mcp.json b/docker/mcp.json new file mode 100644 index 0000000..108f866 --- /dev/null +++ b/docker/mcp.json @@ -0,0 +1,13 @@ +{ + "mcpServers": { + "playwright": { + "command": "npx", + "args": ["@playwright/mcp@latest", "--headless", "--browser", "chromium"] + }, + "cloakbrowser": { + "command": "cloakbrowser-mcp", + "args": [], + "env": { "CLOAKBROWSER_CACHE_DIR": "/cloakbrowser-cache" } + } + } +} diff --git a/docker/worker.Dockerfile b/docker/worker.Dockerfile index 55609ba..8a17589 100644 --- a/docker/worker.Dockerfile +++ b/docker/worker.Dockerfile @@ -7,6 +7,9 @@ RUN apt-get update \ && apt-get install -y --no-install-recommends \ bash ca-certificates git ripgrep curl xz-utils make jq \ golang-go \ + libglib2.0-0 libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 \ + libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \ + libxfixes3 libxrandr2 libgbm1 libpango-1.0-0 libcairo2 libasound2 \ && rm -rf /var/lib/apt/lists/* RUN GOBIN=/usr/local/bin go install golang.org/x/tools/gopls@latest || true @@ -17,6 +20,8 @@ RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent # scripts (husky etc.) crash headless installs. Route installs through # --ignore-scripts so ALL dotfiles packages (pi-subagents, todo tooling, # async agents, every extension) survive the bake. +# MCP servers available to pi agents (matches the host setup). +# cloakbrowser-mcp ships the browser automation MCP (bundled deps, no postinstall). COPY docker/npm-real /usr/local/bin/npm-real COPY docker/npm-shim /usr/local/bin/npm-ignore-scripts # rm first: /usr/local/bin/npm is a symlink into npm's lib dir, and COPY @@ -39,6 +44,12 @@ COPY docker/bridge /bridge # Per-session pi sessions persist here (volume lvmh-sessions); package cache # shared across spawns via volume lvmh-pi-cache at /root/.pi/agent/cache. +# MCP tooling: cloakbrowser-mcp global (bundled deps), npx for playwright, +# and the standard MCP config the pi-mcp-adapter reads. +RUN npm-real install -g --ignore-scripts cloakbrowser-mcp@1.4.0 \ + && mkdir -p /root/.config/mcp +COPY docker/mcp.json /root/.config/mcp/mcp.json + ENV PI_SESSION_DIR=/pi-sessions WORKDIR /workspace CMD ["node", "/bridge/index.mjs"]