A layered devcontainer for Claude Code with browser automation, workflow tooling, and Python/Node development.
| Component | Purpose |
|---|---|
| Claude Code CLI | Anthropic's AI coding assistant |
| Repo Prompt MCP | Context building via Mac host bridge |
| dev-browser | Browser automation skill (SawyerHood) |
| flow-next | Planning/execution workflow (Gordon Mickel) |
| Playwright | E2E testing (pytest-playwright) |
| Python 3.14 + uv | Python development |
| Node 20 + Bun | JavaScript/TypeScript development |
┌─────────────────────────────────────────────────────────────────────────┐
│ LAYER 1: mcr.microsoft.com/playwright:v1.57.0-noble │
│ Ubuntu 24.04 · Node 20 · Chromium · Firefox · WebKit │
├─────────────────────────────────────────────────────────────────────────┤
│ LAYER 2: Anthropic Claude Code essentials │
│ Claude Code CLI · iptables/ipset · firewall script │
├─────────────────────────────────────────────────────────────────────────┤
│ LAYER 3: Common dev tools │
│ zsh · fzf · git-delta · gh · jq · ripgrep · fd │
│ Python 3.14 (via uv) · ruff · Bun │
├─────────────────────────────────────────────────────────────────────────┤
│ LAYER 4: postCreateCommand (always fresh) │
│ flow-next · dev-browser · Repo Prompt MCP config │
└─────────────────────────────────────────────────────────────────────────┘
▲
│ MCP bridge (port 8096)
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ MAC HOST │
│ mcp-proxy :8096 ◄── ~/RepoPrompt/repoprompt_cli │
└─────────────────────────────────────────────────────────────────────────┘
.devcontainer/
├── devcontainer.json
├── Dockerfile
├── init-firewall.sh
└── setup-plugins.sh
# Install
uv tool install mcp-proxy # or: pipx install mcp-proxy
# Copy script
mkdir -p ~/bin
cp mac-host/start-repoprompt-bridge.sh ~/bin/
chmod +x ~/bin/start-repoprompt-bridge.sh
# Run (keep open)
~/bin/start-repoprompt-bridge.shOptional: manage the bridge with a LaunchAgent (manual start/stop, no auto-run on login):
# Install agent (does not start yet)
cp mac-host/com.repoprompt.mcp-bridge.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.repoprompt.mcp-bridge.plist
# Start when needed (e.g., before opening the devcontainer)
launchctl kickstart -k gui/$(id -u)/com.repoprompt.mcp-bridge
# Stop when done (stays stopped)
launchctl stop gui/$(id -u)/com.repoprompt.mcp-bridge
# Uninstall
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/com.repoprompt.mcp-bridge.plist
rm ~/Library/LaunchAgents/com.repoprompt.mcp-bridge.plistCommand Palette → Reopen in Container
claude --dangerously-skip-permissions
/mcp # Verify connections
/flow-next:setup # One-time setupAfter opening the container, run these tests to verify all components work:
# Check versions
node --version # Should show v20.x
python3.14 --version # Should show Python 3.14.x
uv --version # Should show uv 0.x
ruff --version # Should show ruff 0.x
bun --version # Should show bun 1.x
playwright --version # Should show Version 1.57.x
# Check shell
echo $SHELL # Should show /bin/zsh# First, ensure the bridge is running on Mac:
# ~/bin/start-repoprompt-bridge.sh
# Test SSE endpoint (legacy transport)
curl -s http://host.docker.internal:8096/sse | head -1
# Should show: event: endpoint
# Test Streamable HTTP endpoint (preferred)
curl -s -X POST http://host.docker.internal:8096/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# Should return JSON with serverInfo.name: "Repo Prompt"
# In Claude Code, verify MCP is connected
/mcp
# Should list RepoPrompt as connectedMCP Transport Endpoints:
| Endpoint | Method | Purpose |
|---|---|---|
/mcp |
POST | Streamable HTTP transport (preferred) |
/sse |
GET | SSE event stream (returns session endpoint) |
/messages/?session_id=... |
POST | Send messages via SSE transport |
Note:
POST /ssereturns 405 Method Not Allowed - this is expected. Use/mcpfor Streamable HTTP or follow the SSE protocol (GET/sse→ POST to returned endpoint).
# In Claude Code terminal
claude --dangerously-skip-permissions
# Run setup (first time only)
/flow-next:setup
# Test planning (creates .flow/ directory)
/flow-next:plan Create a hello world script
# Verify .flow/ was created
ls -la .flow/# Start a simple test server
echo '<html><body><h1>Test Page</h1><button id="btn">Click me</button></body></html>' > /tmp/test.html
cd /tmp && python3.14 -m http.server 8080 &
# In Claude Code, ask to test it:
"Use dev-browser to check if localhost:8080 has a button with id 'btn'"
# Clean up
pkill -f "python3.14 -m http.server"# Create a simple test
mkdir -p /tmp/playwright-test && cd /tmp/playwright-test
cat > test_example.py << 'EOF'
from playwright.sync_api import sync_playwright
def test_playwright_works():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()
page.goto("https://example.com")
assert "Example Domain" in page.title()
browser.close()
print("Playwright test passed!")
if __name__ == "__main__":
test_playwright_works()
EOF
# Run with Python directly
python3.14 test_example.py
# Or with pytest (if in a project with pytest-playwright)
# pytest test_example.py -v# Should work (allowed domains)
curl -s https://api.anthropic.com -o /dev/null && echo "Anthropic API: OK"
curl -s https://registry.npmjs.org -o /dev/null && echo "npm registry: OK"
# Should fail (blocked)
curl -s --max-time 3 https://example.com || echo "example.com: Blocked (expected)"| Component | Test Command | Expected Result |
|---|---|---|
| Node.js | node --version |
v20.x |
| Python | python3.14 --version |
3.14.x |
| uv | uv --version |
0.x |
| Bun | bun --version |
1.x |
| Playwright | playwright --version |
1.57.x |
| MCP Bridge (SSE) | curl http://host.docker.internal:8096/sse |
event: endpoint |
| MCP Bridge (HTTP) | curl -X POST http://host.docker.internal:8096/mcp -H "Content-Type: application/json" -H "Accept: application/json" -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' |
"name":"Repo Prompt" |
| Firewall | curl https://api.anthropic.com |
Success |
/flow-next:plan Add OAuth login # Creates .flow/ with tasks
/flow-next:work fn-1 # Execute task
/flow-next:interview fn-1 # Refine spec with 40+ questionsJust ask naturally:
"Test the signup flow on localhost:3000"
"Check why the save button isn't working"
"Verify form validation"
/RepoPrompt:rp-build Add user authentication
/RepoPrompt:rp-investigate Why is checkout failing?# Run E2E tests
pytest tests/e2e/
# Run with headed browser
pytest tests/e2e/ --headed| File | Description |
|---|---|
Dockerfile |
4-layer build: Playwright → Claude Code → Dev tools |
devcontainer.json |
Mounts, env vars, VS Code extensions, postCreateCommand |
init-firewall.sh |
Whitelists domains, opens MCP port 8096 |
setup-plugins.sh |
Installs dev-browser, flow-next, configures MCP |
mcr.microsoft.com/playwright:v1.57.0-noble- Pre-built browsers (Chromium, Firefox, WebKit)
- Node 20, npm
- Ubuntu 24.04 LTS
@anthropic-ai/claude-codeCLI- Firewall tools (iptables, ipset, iproute2, dnsutils)
- Sudoers config for firewall script
- Shell: zsh, oh-my-zsh, powerlevel10k, fzf
- Git: git-delta, gh
- CLI: jq, ripgrep, fd, unzip
- Python: uv, Python 3.14, ruff
- JS: Bun (for dev-browser)
Installed fresh on every container start:
- flow-next (Gordon Mickel's marketplace)
- dev-browser (SawyerHood)
- Repo Prompt MCP configuration
Repo Prompt not connecting:
# Test SSE endpoint
curl http://host.docker.internal:8096/sse
# Test Streamable HTTP endpoint (preferred)
curl -X POST http://host.docker.internal:8096/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}'
# Mac - ensure bridge running
~/bin/start-repoprompt-bridge.shPOST /sse returns 405 Method Not Allowed:
This is expected. The /sse endpoint only accepts GET requests. Use /mcp for Streamable HTTP transport instead.
flow-next not found:
/plugin marketplace add https://github.com/gmickel/gmickel-claude-marketplace
/plugin install flow-next
/flow-next:setupdev-browser not working:
cd ~/.claude/skills/dev-browser
bun install && bun run start-serverPython version:
# Check Python version
uv python list
# Use Python 3.14 in project
uv venv --python 3.14
uv sync- Microsoft Playwright - Base image & browser automation
- Anthropic - Claude Code CLI
- SawyerHood - dev-browser skill
- Gordon Mickel - flow-next plugin
- Astral - uv package manager