diff --git a/.github/workflows/runloop-blueprint-template.json b/.github/workflows/runloop-blueprint-template.json index d0fcf57c12..b40821c078 100644 --- a/.github/workflows/runloop-blueprint-template.json +++ b/.github/workflows/runloop-blueprint-template.json @@ -3,6 +3,17 @@ "system_setup_commands": [ "npm i -g @continuedev/cli@latest", "sudo apt update", - "sudo apt install -y ripgrep" - ] + "sudo apt install -y --no-install-recommends ripgrep chromium chromium-driver xvfb", + "sudo mkdir -p /opt/google/chrome", + "sudo ln -s /usr/bin/chromium /opt/google/chrome/chrome" + ], + "launch_parameters": { + "launch_commands": [ + "nohup Xvfb :99 -screen 0 1920x1080x24 > /tmp/xvfb.log 2>&1 &", + "sleep 2" + ], + "environment_variables": { + "DISPLAY": ":99" + } + } } diff --git a/Dockerfile.devbox b/Dockerfile.devbox new file mode 100644 index 0000000000..5cb2d454e0 --- /dev/null +++ b/Dockerfile.devbox @@ -0,0 +1,45 @@ +# Chrome DevTools MCP Development Container +# Based on linux-setup.md instructions + +FROM ubuntu:22.04 + +# Avoid interactive prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive + +# Set up display for headless operation +ENV DISPLAY=:99 + +# Install system dependencies +RUN apt-get update && apt-get install -y \ + chromium-browser \ + chromium-chromedriver \ + xvfb \ + curl \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +# Create symlink for Chrome DevTools MCP +# The MCP looks for Chrome at /opt/google/chrome/chrome +RUN mkdir -p /opt/google/chrome && \ + ln -s /usr/bin/chromium-browser /opt/google/chrome/chrome + +# Create a startup script to run Xvfb +RUN cat > /entrypoint.sh <<'EOF' +#!/bin/bash +# Start Xvfb in the background +Xvfb :99 -screen 0 1920x1080x24 & +# Wait a moment for Xvfb to start +sleep 2 +# Execute the command passed to docker run +exec "$@" +EOF +RUN chmod +x /entrypoint.sh + +# Set working directory +WORKDIR /workspace + +# Use the entrypoint script to ensure Xvfb is running +ENTRYPOINT ["/entrypoint.sh"] + +# Default command (can be overridden) +CMD ["/bin/bash"] diff --git a/RUNLOOP_BLUEPRINT_README.md b/RUNLOOP_BLUEPRINT_README.md new file mode 100644 index 0000000000..95efae12ef --- /dev/null +++ b/RUNLOOP_BLUEPRINT_README.md @@ -0,0 +1,229 @@ +# Chrome DevTools MCP Runloop Blueprint + +This directory contains scripts to create a Runloop Blueprint for Chrome DevTools MCP development in headless Linux environments. + +## What This Blueprint Provides + +The blueprint creates a pre-configured development environment with: + +### Base Tools (from existing blueprint) + +- **Continue CLI**: `@continuedev/cli` installed globally +- **ripgrep**: Fast search tool + +### Chrome DevTools MCP Setup + +- **Chromium Browser**: For web automation and testing +- **ChromeDriver**: For Selenium/WebDriver support +- **Xvfb**: Virtual display server for headless operation +- **Symlink Configuration**: Chrome DevTools MCP expects Chrome at `/opt/google/chrome/chrome`, symlinked to Chromium +- **Environment Setup**: `DISPLAY=:99` pre-configured +- **Auto-start Xvfb**: Virtual display automatically starts when devbox launches + +## Files + +- `create_chrome_devtools_blueprint.py` - Python script to create the blueprint +- `create_chrome_devtools_blueprint.ts` - TypeScript script to create the blueprint +- `Dockerfile.devbox` - Original Docker configuration (for reference) +- `linux-setup.md` - Manual setup guide (for reference) + +## Prerequisites + +- Runloop API access and credentials +- Python 3.7+ (for Python script) or Node.js (for TypeScript script) +- Runloop SDK installed: + - Python: `pip install runloop-api-client` + - TypeScript: `npm install @runloop/api-client` + +## Usage + +### Python + +```bash +# Make executable +chmod +x create_chrome_devtools_blueprint.py + +# Run the script +python create_chrome_devtools_blueprint.py +``` + +### TypeScript + +```bash +# Make executable +chmod +x create_chrome_devtools_blueprint.ts + +# Run the script +npx ts-node create_chrome_devtools_blueprint.ts +# or if using plain JavaScript +node create_chrome_devtools_blueprint.js +``` + +### Creating a Devbox from the Blueprint + +Once the blueprint is created, you can launch devboxes with: + +**Python:** + +```python +from runloop_api_client import Runloop + +runloop = Runloop() + +# By blueprint name (gets latest version) +devbox = await runloop.devboxes.create( + blueprint_name="chrome-devtools-mcp" +) + +# By blueprint ID (for specific version) +devbox = await runloop.devboxes.create( + blueprint_id="bpt_xxxxx" +) +``` + +**TypeScript:** + +```typescript +import { Runloop } from "@runloop/api-client"; + +const runloop = new Runloop(); + +// By blueprint name (gets latest version) +const devbox = await runloop.devboxes.create({ + blueprint_name: "chrome-devtools-mcp", +}); + +// By blueprint ID (for specific version) +const devbox = await runloop.devboxes.create({ + blueprint_id: "bpt_xxxxx", +}); +``` + +## Using Chrome DevTools MCP in the Devbox + +Once your devbox is running, Xvfb will already be started. You can use Chrome DevTools MCP tools immediately: + +### Open a webpage + +```bash +# Use MCP tool or direct chromium command +chromium --headless --disable-gpu --screenshot=screenshot.png https://example.com +``` + +### Take a screenshot + +```bash +chromium --headless --disable-gpu --screenshot=screenshot.png --window-size=1440,900 https://example.com +``` + +### Upload screenshots + +```bash +# Upload to tmpfiles.org for sharing +curl -F "file=@screenshot.png" https://tmpfiles.org/api/v1/upload +``` + +## Blueprint Architecture + +The blueprint is based on `runloop:runloop/starter-arm64` and includes: + +1. **System Packages**: Installs via `apt-get` + + - chromium + - chromium-driver + - xvfb + - curl + - ca-certificates + - ripgrep + +2. **NPM Global Packages**: + + - @continuedev/cli@latest + +3. **Configuration**: + + - Creates `/opt/google/chrome/chrome` symlink + - Sets `DISPLAY=:99` environment variable + - Creates startup script at `/home/user/start-xvfb.sh` + +4. **Launch Commands**: + - Starts Xvfb in background on devbox creation + - Waits for Xvfb to be ready + +## Automated Blueprint Updates + +The blueprint is automatically updated on every stable CLI release via the GitHub Actions workflow (`.github/workflows/stable-release.yml`). The blueprint configuration is stored in `.github/workflows/runloop-blueprint-template.json`. + +When a new stable version of `@continuedev/cli` is published, the workflow: + +1. Publishes the new version to npm +2. Creates a GitHub release +3. Publishes an updated "cn" blueprint to Runloop with the latest configuration + +## Troubleshooting + +### Blueprint Build Failed + +Check the build logs: + +```python +blueprint = await runloop.blueprints.from_id("bpt_xxxxx") +log_result = await blueprint.logs() +for log in log_result.logs: + print(f"{log.level}: {log.message}") +``` + +### Xvfb Not Running in Devbox + +Manually start Xvfb: + +```bash +Xvfb :99 -screen 0 1920x1080x24 & +``` + +Or use the startup script: + +```bash +/home/user/start-xvfb.sh +``` + +### Chrome/Chromium Not Found + +Verify the symlink: + +```bash +ls -la /opt/google/chrome/chrome +which chromium +``` + +## Blueprint Updates + +To update the blueprint: + +1. Modify the Dockerfile string in the script +2. Run the script again (it will create a new version) +3. Optionally delete old versions to save storage costs + +Example cleanup: + +```python +# Get all blueprints with this name +blueprints = await runloop.blueprints.list(name='chrome-devtools-mcp') + +# Keep only the newest, delete the rest +sorted_blueprints = sorted(blueprints, key=lambda x: x.created_at, reverse=True) +for old_blueprint in sorted_blueprints[1:]: + await old_blueprint.delete() +``` + +## Cost Optimization + +- Blueprints persist indefinitely and incur storage costs +- Delete unused blueprint versions regularly +- Use blueprint names instead of IDs to always get the latest version + +## Related Documentation + +- [Runloop Blueprints Documentation](https://docs.runloop.ai/devboxes/blueprints) +- [Chrome DevTools MCP Setup Guide](./linux-setup.md) +- [Runloop SDK Reference](https://docs.runloop.ai/) diff --git a/create_chrome_devtools_blueprint.py b/create_chrome_devtools_blueprint.py new file mode 100755 index 0000000000..1b30056509 --- /dev/null +++ b/create_chrome_devtools_blueprint.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +""" +Create a Runloop Blueprint for Chrome DevTools MCP Development +Based on linux-setup.md instructions +""" + +import time +from runloop_api_client import Runloop + +def create_chrome_devtools_blueprint(): + """Create a blueprint with Chrome DevTools MCP setup""" + + # Initialize Runloop client + runloop = Runloop() + + # Dockerfile based on Runloop base image with Chrome DevTools MCP setup + dockerfile = """FROM runloop:runloop/starter-arm64 + +# Set up display for headless operation +ENV DISPLAY=:99 + +# Install system dependencies (including base blueprint tools) +RUN apt-get update && apt-get install -y --no-install-recommends \\ + chromium \\ + chromium-driver \\ + xvfb \\ + curl \\ + ca-certificates \\ + ripgrep \\ + && rm -rf /var/lib/apt/lists/* + +# Install Continue CLI +RUN npm i -g @continuedev/cli@latest + +# Create symlink for Chrome DevTools MCP +# The MCP looks for Chrome at /opt/google/chrome/chrome +RUN mkdir -p /opt/google/chrome && \\ + ln -s /usr/bin/chromium /opt/google/chrome/chrome + +# Create a startup script to run Xvfb +RUN echo '#!/bin/bash\\n\\ +# Start Xvfb in the background\\n\\ +Xvfb :99 -screen 0 1920x1080x24 &\\n\\ +# Wait a moment for Xvfb to start\\n\\ +sleep 2' > /home/user/start-xvfb.sh && \\ + chmod +x /home/user/start-xvfb.sh + +WORKDIR /home/user +""" + + # Create the blueprint + print("Creating Chrome DevTools MCP blueprint...") + blueprint = runloop.blueprints.create( + name="chrome-devtools-mcp", + dockerfile=dockerfile, + launch_parameters={ + "launch_commands": [ + # Start Xvfb on devbox launch + "nohup Xvfb :99 -screen 0 1920x1080x24 > /tmp/xvfb.log 2>&1 &", + # Wait for Xvfb to be ready + "sleep 2" + ] + } + ) + + print(f"Blueprint created with ID: {blueprint.id}") + + # Wait for blueprint to build (with timeout) + print("Waiting for blueprint to build...") + max_retries = 60 # 5 minutes total (60 * 5 seconds) + retries = 0 + + while blueprint.status not in ["ready", "failed"]: + if retries >= max_retries: + raise TimeoutError("Blueprint build timed out after 5 minutes") + time.sleep(5) + # Refresh blueprint info + blueprints = runloop.blueprints.list() + for bp in blueprints: + if bp.id == blueprint.id: + blueprint = bp + break + print(f"Blueprint status: {blueprint.status}") + retries += 1 + + if blueprint.status == "failed": + print("Blueprint build failed. Checking logs...") + log_result = runloop.blueprints.logs(blueprint.id) + for log in log_result.logs: + print(f"{log.level}: {log.message}") + raise RuntimeError("Blueprint build failed") + + print(f"Blueprint build complete! ID: {blueprint.id}") + print("\nTo create a devbox from this blueprint:") + print(f'devbox = blueprint.create_devbox()') + print(f'# or') + print(f'devbox = runloop.devboxes.create(blueprint_id="{blueprint.id}")') + + return blueprint + +if __name__ == "__main__": + try: + create_chrome_devtools_blueprint() + except Exception as e: + print(f"Error creating blueprint: {e}") + exit(1) diff --git a/create_chrome_devtools_blueprint.ts b/create_chrome_devtools_blueprint.ts new file mode 100755 index 0000000000..564a872f91 --- /dev/null +++ b/create_chrome_devtools_blueprint.ts @@ -0,0 +1,111 @@ +#!/usr/bin/env node +/** + * Create a Runloop Blueprint for Chrome DevTools MCP Development + * Based on linux-setup.md instructions + */ + +import { Runloop } from "@runloop/api-client"; + +async function createChromeDevToolsBlueprint() { + // Initialize Runloop client + const runloop = new Runloop(); + + // Dockerfile based on Runloop base image with Chrome DevTools MCP setup + const dockerfile = `FROM runloop:runloop/starter-arm64 + +# Set up display for headless operation +ENV DISPLAY=:99 + +# Install system dependencies (including base blueprint tools) +RUN apt-get update && apt-get install -y --no-install-recommends \\ + chromium \\ + chromium-driver \\ + xvfb \\ + curl \\ + ca-certificates \\ + ripgrep \\ + && rm -rf /var/lib/apt/lists/* + +# Install Continue CLI +RUN npm i -g @continuedev/cli@latest + +# Create symlink for Chrome DevTools MCP +# The MCP looks for Chrome at /opt/google/chrome/chrome +RUN mkdir -p /opt/google/chrome && \\ + ln -s /usr/bin/chromium /opt/google/chrome/chrome + +# Create a startup script to run Xvfb +RUN echo '#!/bin/bash\\n\\ +# Start Xvfb in the background\\n\\ +Xvfb :99 -screen 0 1920x1080x24 &\\n\\ +# Wait a moment for Xvfb to start\\n\\ +sleep 2' > /home/user/start-xvfb.sh && \\ + chmod +x /home/user/start-xvfb.sh + +WORKDIR /home/user +`; + + // Create the blueprint + console.log("Creating Chrome DevTools MCP blueprint..."); + const blueprint = await runloop.blueprints.create({ + name: "chrome-devtools-mcp", + dockerfile: dockerfile, + launch_parameters: { + launch_commands: [ + // Start Xvfb on devbox launch + "nohup Xvfb :99 -screen 0 1920x1080x24 > /tmp/xvfb.log 2>&1 &", + // Wait for Xvfb to be ready + "sleep 2", + ], + }, + }); + + console.log(`Blueprint created with ID: ${blueprint.id}`); + + // Wait for blueprint to build (with timeout) + console.log("Waiting for blueprint to build..."); + let info = await blueprint.get_info(); + const maxRetries = 60; // 5 minutes total (60 * 5 seconds) + let retries = 0; + + while (info.status !== "build_complete" && info.status !== "build_failed") { + if (retries >= maxRetries) { + throw new Error("Blueprint build timed out after 5 minutes"); + } + await new Promise((resolve) => setTimeout(resolve, 5000)); + info = await blueprint.get_info(); + console.log(`Blueprint status: ${info.status}`); + retries++; + } + + if (info.status === "build_failed") { + console.log("Blueprint build failed. Checking logs..."); + const logResult = await blueprint.logs(); + for (const log of logResult.logs) { + console.log(`${log.level}: ${log.message}`); + } + throw new Error("Blueprint build failed"); + } + + console.log(`Blueprint build complete! ID: ${blueprint.id}`); + console.log("\nTo create a devbox from this blueprint:"); + console.log(`const devbox = await blueprint.createDevbox();`); + console.log(`// or`); + console.log( + `const devbox = await runloop.devboxes.create({ blueprint_id: "${blueprint.id}" });`, + ); + + return blueprint; +} + +// Run if executed directly +if (require.main === module) { + createChromeDevToolsBlueprint() + .then(() => process.exit(0)) + .catch((error) => { + console.error("Error creating blueprint:", error); + process.exit(1); + }); +} + +export { createChromeDevToolsBlueprint }; diff --git a/docs/guides/chrome-devtools-mcp-performance.mdx b/docs/guides/chrome-devtools-mcp-performance.mdx index d63e7c5d08..919ac6e227 100644 --- a/docs/guides/chrome-devtools-mcp-performance.mdx +++ b/docs/guides/chrome-devtools-mcp-performance.mdx @@ -50,6 +50,88 @@ For all options, first: +## Headless Linux Setup + + + + Chrome DevTools MCP requires a display to run Chrome/Chromium. On headless Linux systems (like GitHub Actions runners, Runloop devboxes, or Docker containers), you need to set up a virtual display using Xvfb. + + ### Quick Setup for Ubuntu/Debian + + ```bash + # Install Chromium and Xvfb + sudo apt update + sudo apt install -y chromium chromium-driver xvfb + + # Create symlink (Chrome DevTools MCP expects Chrome at /opt/google/chrome/chrome) + sudo mkdir -p /opt/google/chrome + sudo ln -s /usr/bin/chromium /opt/google/chrome/chrome + + # Start virtual display + Xvfb :99 -screen 0 1920x1080x24 & + export DISPLAY=:99 + ``` + + ### Runloop Blueprint + + For Runloop devboxes, we provide pre-configured blueprint scripts that handle all setup automatically: + + **Python:** + ```bash + # From the Continue repository root + python create_chrome_devtools_blueprint.py + ``` + + **TypeScript:** + ```bash + # From the Continue repository root + npx ts-node create_chrome_devtools_blueprint.ts + ``` + + These scripts create a Runloop blueprint with: + - Chromium and ChromeDriver pre-installed + - Xvfb configured and auto-started + - Proper symlinks and environment variables + - Continue CLI with latest version + + See [`RUNLOOP_BLUEPRINT_README.md`](https://github.com/continuedev/continue/blob/main/RUNLOOP_BLUEPRINT_README.md) for detailed documentation. + + ### GitHub Actions Setup + + The Chrome DevTools MCP works out of the box on GitHub Actions `ubuntu-latest` runners with minimal setup: + + ```yaml + steps: + - name: Setup Chrome DevTools Environment + run: | + sudo apt update + sudo apt install -y chromium chromium-driver xvfb + sudo mkdir -p /opt/google/chrome + sudo ln -s /usr/bin/chromium /opt/google/chrome/chrome + + - name: Start Virtual Display + run: | + Xvfb :99 -screen 0 1920x1080x24 & + sleep 2 + echo "DISPLAY=:99" >> $GITHUB_ENV + ``` + + The Continue repository's stable release workflow automatically updates a Runloop blueprint with these configurations. See [`.github/workflows/runloop-blueprint-template.json`](https://github.com/continuedev/continue/blob/main/.github/workflows/runloop-blueprint-template.json). + + ### Troubleshooting + + **"Missing X server" error:** + - Verify Xvfb is running: `ps aux | grep Xvfb` + - Check DISPLAY variable: `echo $DISPLAY` + + **"Could not find Chrome executable" error:** + - Verify symlink: `ls -la /opt/google/chrome/chrome` + - Check Chromium installation: `which chromium` + + For detailed Linux setup instructions, see [`linux-setup.md`](https://github.com/continuedev/continue/blob/main/linux-setup.md). + + + ## Chrome DevTools MCP Workflow Options diff --git a/linux-setup.md b/linux-setup.md new file mode 100644 index 0000000000..7b306b0e8c --- /dev/null +++ b/linux-setup.md @@ -0,0 +1,99 @@ +# Chrome DevTools MCP Setup Guide for Headless Linux + +This guide covers how to set up the Chrome DevTools MCP server on a headless Linux system, particularly for ARM64 architecture. + +## Prerequisites + +- Linux system (Debian/Ubuntu-based) +- Claude Code installed +- sudo access + +## Installation Steps + +### 1. Install Chromium Browser + +On ARM64 Linux, Google Chrome is not officially supported, so we use Chromium instead: + +```bash +sudo apt update +sudo apt install -y --no-install-recommends chromium chromium-driver +``` + +**Note:** We use `--no-install-recommends` to avoid Python version conflicts with the base system. + +### 2. Create Symlink for Chrome DevTools MCP + +The Chrome DevTools MCP looks for Chrome at `/opt/google/chrome/chrome`. Create a symlink to Chromium: + +```bash +sudo mkdir -p /opt/google/chrome +sudo ln -s /usr/bin/chromium /opt/google/chrome/chrome +``` + +### 3. Install Xvfb (Virtual Display) + +Since this is a headless system, we need a virtual display: + +```bash +sudo apt install -y xvfb +``` + +### 4. Start Xvfb + +Start Xvfb on display :99 with standard resolution: + +```bash +Xvfb :99 -screen 0 1920x1080x24 & +``` + +**Note:** This needs to be running whenever you want to use Chrome DevTools MCP. You can add this to your shell startup script or create a systemd service for persistence. + +## Usage + +Once configured, you can use Chrome DevTools MCP tools: + +### Open a webpage: + +``` +Use the chrome-devtools new_page tool to visit https://example.com +``` + +### Take a screenshot: + +``` +Use the chrome-devtools take_screenshot tool (saving it as a file) +``` + +### Resize browser window: + +``` +Use the chrome-devtools resize_page tool with width and height **1440x900** (MacBook, 16:10) +``` + +## Uploading Screenshots + +To share screenshots from a headless system, you can use file sharing services: + +```bash +# Upload to tmpfiles.org +curl -F "file=@/path/to/screenshot.png" https://tmpfiles.org/api/v1/upload +``` + +## Troubleshooting + +### "Missing X server" error + +- Make sure Xvfb is running: `ps aux | grep Xvfb` +- Verify the DISPLAY variable is set in your MCP config +- Restart Claude Code after config changes + +### "Could not find Google Chrome executable" error + +- Verify the symlink exists: `ls -la /opt/google/chrome/chrome` +- Make sure Chromium is installed: `which chromium` + +### Architecture issues (x86_64 vs ARM64) + +- Check your architecture: `uname -m` +- If ARM64 (aarch64), use Chromium instead of Chrome +- If x86_64, you can install Google Chrome directly from https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb