swisstech.net

tech and photography

Sandboxing Gemini CLI and Playwright MCP

2025-12-12

Since the hype around claude code and others has subsided a bit, I figured its time to give one of these tools a try and gemini-cli being free, I decided to use that for a bit.

Installation is merely an npm install or npx call away. Once gemini is launched, use /auth and login with your personal google account - easy.

Things went well and I had it extend an existing little app of mine but soon enough I realized it could only fix compile errors but not layout or styling issues or runtime errors. So, we need a way to give gemini-cli a browser and eyes and it seems playwright is the go-to tool for that.

Playwright also is just an npm install or npx call away from running but since starting to dabble with gemini and now, just before wanting to install playwright, a little thing with some mildly popular npm packages happended and once again refreshed my weariness regarding installing random npm packages (and potentially running equally random postinstall scripts) - stuff like this really puts a damper on the joy of developing software.

Since I’ve been running gemini with --sandbox anyway, I decided, I wanted playwright to run in isolation too. Being new to all this MCP stuff, unless I missed something, it took quite a bit of experimentation to get this all working the way I want it to.

Playwright Docker

When initially running playwright in docker using it via gemini I got weird errors, that it had no permissions to access my $HOME directory - why would it want to do that and how does this dockerized playwright know about my username anyways? I couldn’t figure out why and how it knew about my home directory but --user root would at least ‘fix’ the permissions issue. Now, when taking a screenshot, it would unfortunately be saved inside the docker container, in the same path as from where i started gemini, in a subdirectory named .playwright-mcp. To solve that, I did a binding mount of the project directory onto the same location inside the docker container. That would allow playwright to run isolated yet on the outside everything would still look like it ran directly and was able to access the .playwright-mcp directory directly.

run playwright

1
2
3
4
5
6
7
docker run --rm --init --pull=always \
    -p 8931:8931 \
    --user root \ # CHECK if this is still needed
    -v $(pwd)/.playwright-mcp:$(pwd)/.playwright-mcp \
    --name playwright-mcp mcr.microsoft.com/playwright/mcp \
    --port 8931 \
    --host 0.0.0.0

get ip of the playwright container

1
docker inspect bridge

gemini config

The IP address below is the IP of the playwright container. Ideally, this IP would be read from a command and automatically update the gemini config.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "theme": "ANSI Light",
  "selectedAuthType": "oauth-personal",
  "mcpServers": {
    "playwright": {
      "httpUrl": "http://172.17.0.2:8931/mcp",
      "timeout": 30000
    }
  }
}

Gemini Sandbox

Now, I run gemini --sandbox, which means I’m still running whatever random code is included in google’s gemini distribution, which then eventually runs a docker container for its actions - at least that’s my current understanding on how this works.

Improving the setup

Now manually managing IPs and ports and making sure the container is running and still has the correct IP written in the config gets old pretty quick, so i ended up throwing all of the above into a docker-compose with a few companion scripts that keep the gemini version up to date.

https://github.com/stackmagic/gemini-docker

So far it works and there’s probably still tons to improve. Also, docker isn’t really a security boundary so it’s not perfect protection anyway, but better than nothing.