Docker¶
The published image is the recommended runtime for repeatable MCP usage.
Use the Docker Streamable HTTP reverse proxy recipe when you want a short deployment path for an already-running HTTP server behind TLS and authentication.
Run¶
Artifacts are written to /data in the container. Mount that path to keep screenshots, snapshots, downloads, and network output.
--init is recommended because browser automation can create short-lived child processes. Docker's init process reaps those children cleanly.
The same release tags are published to Docker Hub as swimmwatch/cloakbrowser-mcp and to GHCR as ghcr.io/swimmwatch/cloakbrowser-mcp.
Persistent Profiles¶
Docker does not enable a persistent browser profile by default. Use the existing /data volume as the persistence root when you want cookies, local storage, cache, or extension state to survive container restarts:
docker run --rm --init -i \
-e PLAYWRIGHT_MCP_USER_DATA_DIR=/data/profiles/default \
-v "$PWD/artifacts:/data" \
swimmwatch/cloakbrowser-mcp:latest
Environment variables inside Docker must use container paths such as /data/profiles/default, not host paths. The bridge creates the profile directory if it is missing, verifies it is writable, writes the container path to generated Playwright MCP config, and rejects duplicate active profile directories inside one server process.
Chrome Extensions¶
Chrome extensions require a persistent profile and must be mounted separately. Use container paths in environment variables, not host paths. The extension mount can be read-only:
docker run --rm --init -i \
-e PLAYWRIGHT_MCP_USER_DATA_DIR=/data/profiles/default \
-e CLOAK_PLAYWRIGHT_MCP_EXTENSION_PATHS=/extensions/my-extension \
-v "$PWD/artifacts:/data" \
-v "$PWD/extensions/my-extension:/extensions/my-extension:ro" \
swimmwatch/cloakbrowser-mcp:latest
Use a JSON array for CLOAK_PLAYWRIGHT_MCP_EXTENSION_PATHS when a path contains commas or when passing multiple extension directories. Restart the container after changing extension files or extension paths.
Streamable HTTP¶
For local Streamable HTTP usage, publish the container port on loopback:
docker run --rm --init -p 127.0.0.1:3000:3000 \
-v "$PWD/artifacts:/data" \
swimmwatch/cloakbrowser-mcp:latest \
--transport streamable-http --http-host 0.0.0.0 --http-port 3000
curl http://127.0.0.1:3000/healthz
curl http://127.0.0.1:3000/readyz
For direct HTTPS from the container, mount your certificate files and select HTTPS:
docker run --rm --init -p 127.0.0.1:3000:3000 \
-v "$PWD/artifacts:/data" \
-v "$PWD/certs:/certs:ro" \
swimmwatch/cloakbrowser-mcp:latest \
--transport streamable-http --http-host 0.0.0.0 --http-port 3000 \
--http-protocol https --https-cert /certs/cert.pem --https-key /certs/key.pem
The host-side 127.0.0.1:3000 bind keeps the endpoint local. If you publish Streamable HTTP on a non-loopback interface, use HTTPS plus authentication, or place the server behind a trusted TLS-terminating reverse proxy with authentication and network controls. Streamable HTTP exposes fixed GET /healthz and GET /readyz probes on the same host and port. If --http-auth-token or CLOAK_PLAYWRIGHT_MCP_HTTP_AUTH_TOKEN is configured, the probes require the same Authorization: Bearer ... header as MCP requests. See the generated CLI Reference for all HTTP transport flags and environment variables.
For a complete reverse-proxy-oriented example, see Docker Streamable HTTP Behind Reverse Proxy.
GeoIP Proxy Matching¶
Docker uses the same proxy and GeoIP environment variables as npm. Enable GeoIP proxy matching when regional QA needs CloakBrowser timezone, language, and locale fingerprints to follow the configured proxy location:
docker run --rm --init -i \
-e PLAYWRIGHT_MCP_PROXY_SERVER="http://user:pass@proxy.example:8080" \
-e CLOAK_PLAYWRIGHT_MCP_GEOIP_PROXY_MATCH=true \
-v "$PWD/artifacts:/data" \
swimmwatch/cloakbrowser-mcp:latest
For authenticated proxies, embed credentials in the proxy URL and percent-encode special characters in the username or password.
When the container runs Streamable HTTP, clients can also choose different proxies per MCP session through initialize metadata. See GeoIP Proxy Matching for runtime proxy metadata, multi-region use cases, limitations, and the regional QA recipe.
Defaults¶
| Variable | Default |
|---|---|
PLAYWRIGHT_MCP_BROWSER_ENGINE | cloak |
PLAYWRIGHT_MCP_HEADLESS | true |
PLAYWRIGHT_MCP_OUTPUT_DIR | /data |
PLAYWRIGHT_MCP_OUTPUT_MODE | stdout |
PLAYWRIGHT_MCP_USER_DATA_DIR | unset |
CLOAK_PLAYWRIGHT_MCP_TRANSPORT | stdio |
CLOAK_PLAYWRIGHT_MCP_HTTP_PROTOCOL | http |
CLOAK_PLAYWRIGHT_MCP_HTTP_HOST | 127.0.0.1 |
CLOAK_PLAYWRIGHT_MCP_HTTP_PORT | 3000 |
CLOAK_PLAYWRIGHT_MCP_HTTP_ENDPOINT | /mcp |
CLOAK_PLAYWRIGHT_MCP_HTTP_AUTH_TOKEN | unset |
CLOAK_PLAYWRIGHT_MCP_HTTP_SESSION_BACKEND | memory |
CLOAK_PLAYWRIGHT_MCP_HTTP_SESSION_IDLE_TTL_MS | 3600000 |
CLOAK_PLAYWRIGHT_MCP_HTTP_SESSION_MAX | 32 |
CLOAK_PLAYWRIGHT_MCP_LOG_LEVEL | info |
CLOAK_PLAYWRIGHT_MCP_GEOIP_PROXY_MATCH | false |
CLOAK_PLAYWRIGHT_MCP_CONTEXT_OPTIONS | unset |
CLOAK_PLAYWRIGHT_MCP_EXTENSION_PATHS | unset |
CLOAK_PLAYWRIGHT_MCP_CONSOLE_FALLBACK | true |
CLOAK_PLAYWRIGHT_MCP_STEALTH_ARGS | true |
CLOAK_PLAYWRIGHT_MCP_NO_SANDBOX | true |
MCP Client Config¶
{
"mcpServers": {
"cloakbrowser": {
"command": "docker",
"args": [
"run",
"--rm",
"--init",
"-i",
"-v",
"/tmp/cloakbrowser-artifacts:/data",
"swimmwatch/cloakbrowser-mcp:latest"
]
}
}
}
Build Locally¶
The Dockerfile uses the pinned official Playwright MCP image as the runtime base, applies available Debian security updates during the build, removes the unused global npm payload from the runtime image, and installs the bridge under /opt/cloakbrowser-mcp.
The release workflow publishes SBOM and provenance attestations, includes OCI labels for source, revision, version, license, base image name, and base image digest, and scans the built image with Trivy before publishing.