-
Notifications
You must be signed in to change notification settings - Fork 3.9k
feat: add Chrome DevTools MCP Runloop blueprint and headless Linux docs #9005
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
sestinj
wants to merge
6
commits into
main
Choose a base branch
from
nate/cdt-blueprint
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+685
−2
Open
Changes from 3 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
f783290
feat: chrome devtools blueprint
sestinj 6748dde
move to blueprint json
sestinj 637ce0f
docs: add headless Linux and Runloop blueprint setup to Chrome DevToo…
continue[bot] 0c975cf
fix: persist DISPLAY env var across GitHub Actions steps
continue[bot] a693347
fix: improve Dockerfile and blueprint script robustness
continue[bot] 660919e
fix: throw errors on blueprint timeout/failure for proper exit codes
continue[bot] File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| # 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 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\n\ | ||
| # Execute the command passed to docker run\n\ | ||
| exec "$@"' > /entrypoint.sh && \ | ||
| 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"] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 "[email protected]" 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/) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| #!/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 sudo mkdir -p /opt/google/chrome && \\ | ||
| sudo 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 | ||
| print("Waiting for blueprint to build...") | ||
|
|
||
| while blueprint.status not in ["ready", "failed"]: | ||
| 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}") | ||
|
|
||
| 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}") | ||
| return None | ||
|
|
||
| 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__": | ||
| create_chrome_devtools_blueprint() |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: The entrypoint script creation using
echowith\nescape sequences is fragile and shell-dependent. Consider using a heredoc for better readability and portability:Prompt for AI agents
✅ Addressed in
a693347