Safely navigate, interact with, and capture evidence from web pages using automated browser operations.
Navigate, interact with, and capture evidence from web pages for testing and monitoring. Use it to verify preview deployments, run Lighthouse audits, or test user flows when you need to visually inspect or scrape public data from websites.
/plugin marketplace add tstomtimes/orchestra/plugin install orchestra@orchestra-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Safely navigate, interact with, and capture evidence from web pages using automated browser operations.
artifacts/browser/{session}/operations.logNo configuration required! The browser server allows access to any valid URL and shows the browser GUI by default for easy debugging.
Default settings in .env:
# Server port (default: 9222 - Chrome DevTools Protocol port)
BROWSER_MCP_PORT=9222
# Show browser GUI (default: visible for debugging)
# Set to 'true' for headless/background mode (useful for CI)
BROWSER_HEADLESS=false
To run browser in the background without GUI:
# Option 1: Set environment variable
BROWSER_HEADLESS=true npm run browser
# Option 2: Update .env file
BROWSER_HEADLESS=true
By default (GUI mode):
curl -X POST http://localhost:3030/init
# Response: {"ok": true, "sessionId": "1234567890"}
curl -X POST http://localhost:3030/navigate \
-H 'Content-Type: application/json' \
-d '{"url": "https://myapp.vercel.app", "waitUntil": "networkidle"}'
# Response: {"ok": true, "url": "https://myapp.vercel.app", "title": "My App"}
curl -X POST http://localhost:3030/click \
-H 'Content-Type: application/json' \
-d '{"selector": "button.primary"}'
# Response: {"ok": true}
curl -X POST http://localhost:3030/type \
-H 'Content-Type: application/json' \
-d '{"selector": "input[name=search]", "text": "test query", "pressEnter": true}'
# Response: {"ok": true}
Interactive Authentication Flow:
curl -X POST http://localhost:3030/auth \
-H 'Content-Type: application/json' \
-d '{"type": "shopify-store", "submitSelector": "button[type=submit]"}'
{
"needsPassword": true,
"envVarName": "SHOPIFY_STORE_PASSWORD",
"type": "shopify-store",
"prompt": "Please enter the password for shopify-store:"
}
curl -X POST http://localhost:3030/auth \
-H 'Content-Type: application/json' \
-d '{"type": "shopify-store", "password": "mypassword", "submitSelector": "button[type=submit]"}'
# Response: {"ok": true, "shouldSavePassword": true, "envVarName": "SHOPIFY_STORE_PASSWORD"}
curl -X POST http://localhost:3030/auth/save \
-H 'Content-Type: application/json' \
-d '{"envVarName": "SHOPIFY_STORE_PASSWORD", "password": "mypassword"}'
# Response: {"ok": true, "message": "Password saved to .env as SHOPIFY_STORE_PASSWORD"}
Built-in auth types:
shopify-store - Uses SHOPIFY_STORE_PASSWORD environment variablestaging - Uses STAGING_PASSWORD environment variablepreview - Uses PREVIEW_PASSWORD environment variable{TYPE}_PASSWORD (e.g., my-site → MY_SITE_PASSWORD)Parameters:
type (required): Auth type (built-in or custom)passwordSelector (optional): CSS selector for password field (default: input[type="password"])submitSelector (optional): CSS selector for submit button (if provided, auto-submits form)password (optional): Password to use (if not in environment variable)Using browser-helper.sh (handles interactive flow automatically):
# Will prompt for password if not in .env, then offer to save it
# Also handles 2FA automatically - waits for user to complete 2FA in browser
./browser-helper.sh auth shopify-store "input[type=password]" "button[type=submit]"
If 2FA is detected after password authentication, the system will automatically wait for manual completion:
curl -X POST http://localhost:3030/auth/wait-2fa \
-H 'Content-Type: application/json' \
-d '{"timeout": 120000}'
# Response: {"ok": true, "completed": true, "message": "2FA completed successfully", "url": "..."}
How it works:
requires2FA: true responseParameters:
timeout (optional): Maximum wait time in milliseconds (default: 120000 = 2 minutes)expectedUrlPattern (optional): Regex pattern for successful auth URLcurl -X POST http://localhost:3030/wait \
-H 'Content-Type: application/json' \
-d '{"selector": ".results", "timeout": 10000}'
# Response: {"ok": true}
curl -X POST http://localhost:3030/scrape \
-H 'Content-Type: application/json' \
-d '{"selector": "h2.product-title", "limit": 50}'
# Response: {"ok": true, "data": ["Product 1", "Product 2", ...]}
curl -X POST http://localhost:3030/screenshot \
-H 'Content-Type: application/json' \
-d '{"filename": "homepage.png", "fullPage": true}'
# Response: {"ok": true, "path": "/path/to/artifacts/browser/.../homepage.png"}
curl -X POST http://localhost:3030/content
# Response: {"ok": true, "url": "...", "title": "...", "html": "..."}
curl -X POST http://localhost:3030/evaluate \
-H 'Content-Type: application/json' \
-d '{"expression": "document.querySelectorAll(\"img\").length"}'
# Response: {"ok": true, "result": 42}
curl -X POST http://localhost:3030/close
# Response: {"ok": true}
curl http://localhost:3030/health
# Response: {"ok": true, "browser": true, "page": true, "sessionId": "...", "allowedDomains": [...]}
#!/usr/bin/env bash
PREVIEW_URL="https://myapp-pr-123.vercel.app"
# Initialize
curl -X POST http://localhost:3030/init
# Navigate
curl -X POST http://localhost:3030/navigate \
-H 'Content-Type: application/json' \
-d "{\"url\":\"$PREVIEW_URL\"}"
# Wait for main content
curl -X POST http://localhost:3030/wait \
-H 'Content-Type: application/json' \
-d '{"selector":"main"}'
# Screenshot
curl -X POST http://localhost:3030/screenshot \
-H 'Content-Type: application/json' \
-d '{"filename":"preview-homepage.png","fullPage":true}'
# Close
curl -X POST http://localhost:3030/close
#!/usr/bin/env bash
# Initialize
curl -X POST http://localhost:3030/init
# Navigate to competitor site (must be in allowlist!)
curl -X POST http://localhost:3030/navigate \
-H 'Content-Type: application/json' \
-d '{"url":"https://competitor.shopify.com/products"}'
# Scrape product titles
curl -X POST http://localhost:3030/scrape \
-H 'Content-Type: application/json' \
-d '{"selector":"h3.product-title","limit":20}' \
> artifacts/competitor-products.json
# Close
curl -X POST http://localhost:3030/close
#!/usr/bin/env bash
PREVIEW_URL="https://myapp.vercel.app"
# Initialize
curl -X POST http://localhost:3030/init
# Navigate
curl -X POST http://localhost:3030/navigate \
-H 'Content-Type: application/json' \
-d "{\"url\":\"$PREVIEW_URL\"}"
# Type in search box
curl -X POST http://localhost:3030/type \
-H 'Content-Type: application/json' \
-d '{"selector":"input[type=search]","text":"test product","pressEnter":true}'
# Wait for results
curl -X POST http://localhost:3030/wait \
-H 'Content-Type: application/json' \
-d '{"selector":".search-results"}'
# Screenshot results
curl -X POST http://localhost:3030/screenshot \
-H 'Content-Type: application/json' \
-d '{"filename":"search-results.png","fullPage":false}'
# Close
curl -X POST http://localhost:3030/close
#!/usr/bin/env bash
STORE_URL="https://mystore.myshopify.com"
# Initialize
curl -X POST http://localhost:3030/init
# Navigate to password-protected store
curl -X POST http://localhost:3030/navigate \
-H 'Content-Type: application/json' \
-d "{\"url\":\"$STORE_URL\"}"
# Wait for password form
curl -X POST http://localhost:3030/wait \
-H 'Content-Type: application/json' \
-d '{"selector":"input[type=password]"}'
# Authenticate using environment variable
AUTH_RESPONSE=$(curl -s -X POST http://localhost:3030/auth \
-H 'Content-Type: application/json' \
-d '{"type":"shopify-store","submitSelector":"button[type=submit]"}')
# Check if 2FA is required
REQUIRES_2FA=$(echo "$AUTH_RESPONSE" | jq -r '.requires2FA // false')
if [ "$REQUIRES_2FA" = "true" ]; then
echo "⏳ 2FA detected. Please complete authentication in the browser..."
# Wait for 2FA completion (2 minutes timeout)
curl -s -X POST http://localhost:3030/auth/wait-2fa \
-H 'Content-Type: application/json' \
-d '{"timeout":120000}' | jq
echo "✅ 2FA completed"
fi
# Wait for store to load
curl -X POST http://localhost:3030/wait \
-H 'Content-Type: application/json' \
-d '{"selector":"main"}'
# Screenshot the store
curl -X POST http://localhost:3030/screenshot \
-H 'Content-Type: application/json' \
-d '{"filename":"shopify-store.png","fullPage":true}'
# Close
curl -X POST http://localhost:3030/close
Or simply use browser-helper.sh (handles everything automatically):
./browser-helper.sh init
./browser-helper.sh navigate https://mystore.myshopify.com
./browser-helper.sh wait "input[type=password]"
./browser-helper.sh auth shopify-store "input[type=password]" "button[type=submit]"
# If 2FA detected, automatically prompts user and waits for completion
./browser-helper.sh wait "main"
./browser-helper.sh screenshot shopify-store.png true
./browser-helper.sh close
#!/usr/bin/env bash
if [ -n "$PREVIEW_URL" ]; then
echo "→ Capturing preview screenshot..."
curl -s -X POST http://localhost:3030/init > /dev/null
curl -s -X POST http://localhost:3030/navigate \
-H 'Content-Type: application/json' \
-d "{\"url\":\"$PREVIEW_URL\"}" > /dev/null
curl -s -X POST http://localhost:3030/screenshot \
-H 'Content-Type: application/json' \
-d '{"filename":"preview.png","fullPage":true}' | jq -r '.path'
curl -s -X POST http://localhost:3030/close > /dev/null
echo "✅ Screenshot saved"
fi
#!/usr/bin/env bash
PROD_URL="${PRODUCTION_URL:-https://app.example.com}"
echo "→ Running production health check..."
curl -s -X POST http://localhost:3030/init > /dev/null
curl -s -X POST http://localhost:3030/navigate \
-H 'Content-Type: application/json' \
-d "{\"url\":\"$PROD_URL\"}" > /dev/null
# Check if critical element exists
RESULT=$(curl -s -X POST http://localhost:3030/evaluate \
-H 'Content-Type: application/json' \
-d '{"expression":"document.querySelector(\".app-loaded\") !== null"}' | jq -r '.result')
if [ "$RESULT" = "true" ]; then
echo "✅ Production app loaded successfully"
else
echo "❌ Production app failed to load"
exit 1
fi
curl -s -X POST http://localhost:3030/close > /dev/null
/init before any browser operations/close when done to free resources/wait before interacting with dynamic contentAdd the domain to BROWSER_ALLOWED_DOMAINS in .env
Call /init endpoint first
You've hit the 10 navigation limit per session. Close and reinitialize.
Navigate to a URL first using /navigate
Check that artifacts/ directory has write permissions
All browser operations save artifacts to:
artifacts/browser/{sessionId}/
├── operations.log # All operations with timestamps
├── screenshot.png # Screenshots (custom filenames)
├── preview.png
└── ...
Input containing these patterns will be rejected:
password (case-insensitive)credit cardssn / social securityBlocked keywords in /evaluate:
deletedropremovecookielocalStorageUse when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.