Complete guide to using Model Context Protocol (MCP) with Claude for intelligent test generation and execution.
- Overview
- Installation & Setup
- Quick Start
- Test Generation
- Guided Execution
- CLI Usage
- API Usage
- Configuration
- Advanced Features
- Troubleshooting
- Best Practices
UITestFlow integrates with Anthropic's Model Context Protocol (MCP) to enable:
- Natural Language Test Generation: Describe your test in plain English, get a complete test definition
- Intelligent Selector Adaptation: Claude suggests alternative selectors when elements aren't found
- Error Recovery: Automatic recovery from test failures with Claude's guidance
- Step Validation: Real-time validation of test steps with page state analysis
┌─────────────────┐
│ UITestFlow CLI │
│ or API │
└────────┬────────┘
│
▼
┌─────────────────────────────────────┐
│ Claude Client (Anthropic API) │
│ • Test generation │
│ • Guidance during execution │
└────────┬───────────────┬────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌──────────────────┐
│ MCP Playwright │ │ Test Runner │
│ Server │ │ • Step execution│
│ • Browser │ │ • Page state │
│ automation │ │ • Screenshots │
│ • Tools │ └──────────────────┘
└─────────────────┘
- Faster Test Creation: Write tests in natural language instead of code
- More Robust Tests: Adaptive selectors reduce flakiness
- Better Debugging: Claude provides context-aware error messages
- Lower Maintenance: Auto-recovery from common failures
- Python 3.11 or higher
- UITestFlow installed (
pip install uitestflow) - Anthropic API key
- Go to https://console.anthropic.com/settings/keys
- Create a new API key
- Save it securely
Option 1: Environment Variable (Recommended)
export ANTHROPIC_API_KEY="your-api-key-here"Add to your shell profile (.bashrc, .zshrc, etc.) for persistence.
Option 2: .env File
# Copy example file
cp .env.mcp.example .env
# Edit and add your key
echo "ANTHROPIC_API_KEY=your-api-key-here" >> .envOption 3: Configuration File
# .uitest_profiles.yaml
default: local
profiles:
local:
anthropic_api_key: "your-api-key-here"
# ... other settings# In your .env file
UITESTFLOW_MCP_ENABLED=true
UITESTFLOW_MCP_CLAUDE_ENABLED=true
UITESTFLOW_MCP_GENERATION_ENABLED=true
UITESTFLOW_MCP_GUIDED_EXECUTION_ENABLED=trueuitestflow config validateExpected output:
✓ Configuration valid
✓ MCP features enabled
✓ Claude API key configured
✓ MCP server available
uitestflow generate "Login to http://example.com with admin/password123" \
--output login_test.yamlThis creates a complete test file:
version: "1.0"
name: "login_test"
steps:
- name: "Navigate to login page"
action: "goto"
url: "http://example.com/login"
- name: "Fill username"
action: "fill"
selector: 'input[name="username"]'
value: "admin"
# ... more stepsuitestflow run login_test.yamluitestflow run login_test.yaml --with-claudeThis enables:
- Real-time selector adaptation
- Error recovery
- Step validation
- Enhanced logging
uitestflow generate "DESCRIPTION" --output FILEExample: Simple Form
uitestflow generate "Fill out contact form with name 'John', email '[email protected]', submit" \
--output contact_form.yaml \
--base-url "http://example.com"uitestflow generate "DESCRIPTION" \
--output FILE # Output file path
--base-url URL # Base URL for test
--model MODEL # Claude model (haiku/sonnet/opus)
--temperature TEMP # Creativity (0.0-1.0)
--dry-run # Preview without saving
--verbose # Show generation detailsclaude-3-5-haiku-20241022 (Default, Recommended)
- Fastest generation
- Most cost-effective
- Great for simple to moderate complexity tests
claude-3-5-sonnet-20241022
- Balanced performance
- Better for complex multi-step workflows
- More detailed evaluators
claude-3-opus-20240229
- Most capable
- Best for complex scenarios with many edge cases
- Highest cost
# Use Haiku for simple tests
uitestflow generate "Login test" --model claude-3-5-haiku-20241022
# Use Sonnet for complex workflows
uitestflow generate "Complete checkout flow with multiple items" \
--model claude-3-5-sonnet-20241022Temperature affects creativity and variability:
- 0.0-0.3: Deterministic, consistent (recommended for tests)
- 0.3-0.5: Slightly varied, balanced
- 0.5-1.0: Creative, more varied (not recommended for tests)
# Deterministic generation
uitestflow generate "Login test" --temperature 0.2
# More creative (use cautiously)
uitestflow generate "Explore dashboard features" --temperature 0.5Be Specific
❌ "Test the login"
✅ "Navigate to /login, fill username 'admin' and password 'pass123', click submit, verify dashboard loads"
Include Expected Outcomes
❌ "Search for products"
✅ "Search for 'laptop', verify at least 5 results appear, click first result"
Mention Important Selectors
❌ "Click the button"
✅ "Click the submit button with data-testid='submit-btn'"
Describe User Flow
❌ "Test checkout"
✅ "Add item to cart, go to checkout, fill shipping info, select payment method, complete order"
Login Test
uitestflow generate "Navigate to login page, fill username and password from config, click login button, wait for dashboard to load" \
--output tests/login.yamlForm Submission
uitestflow generate "Fill registration form with firstName, lastName, email, password, confirmPassword, accept terms checkbox, submit, verify success message" \
--output tests/registration.yamlSearch and Filter
uitestflow generate "Search for 'wireless mouse', apply price filter $20-$50, apply brand filter 'Logitech', verify results update, click first result" \
--output tests/search.yamlMulti-Page Navigation
uitestflow generate "Login, navigate to Settings, change notification preferences, save, verify success message, navigate back to dashboard" \
--output tests/settings.yamluitestflow run test.yaml --with-claudeFor each test step:
-
Pre-Step Analysis
- Captures current page state (URL, title, console logs)
- Sends to Claude for context
-
Step Execution
- Executes the step normally
- If selector fails, requests alternatives from Claude
-
Post-Step Validation
- Claude validates the step completed successfully
- Checks for unexpected page changes or errors
-
Error Recovery (if step fails)
- Sends error and page state to Claude
- Claude suggests recovery actions
- Attempts recovery automatically
When a selector doesn't work, Claude suggests alternatives:
Original selector: button.submit
❌ Failed: Element not found
Claude suggestions:
1. button[type="submit"]
2. input[type="submit"]
3. .btn-primary
Trying alternative 1...
✅ Success: button[type="submit"]
The working selector is saved for future runs.
Timing Issues
Error: Element not visible yet
Recovery: Wait additional 2 seconds
Result: ✅ Element now visible, continuing
Navigation
Error: Unexpected redirect to /confirm
Recovery: Accept the redirect, continue test
Result: ✅ Adapted to new flow
Form Validation
Error: Form validation error appeared
Recovery: Clear the field and re-enter value
Result: ✅ Validation passed
Control guidance behavior in .env:
# Detail level: minimal, standard, verbose
UITESTFLOW_MCP_GUIDANCE_DETAIL_LEVEL=standard
# Max retries per step
UITESTFLOW_MCP_GUIDANCE_MAX_RETRIES=3
# Timeout for guidance requests
UITESTFLOW_MCP_GUIDANCE_TIMEOUT=30
# Enable selector adaptation
UITESTFLOW_MCP_SELECTOR_ADAPTATION_ENABLED=true
UITESTFLOW_MCP_SELECTOR_MAX_ALTERNATIVES=3
# Enable error recovery
UITESTFLOW_MCP_ERROR_RECOVERY_ENABLED=true
UITESTFLOW_MCP_ERROR_RECOVERY_MAX_ATTEMPTS=2uitestflow generate DESCRIPTION [OPTIONS]Options:
--output FILE: Save to file (default: stdout)--base-url URL: Base URL for test--model MODEL: Claude model to use--temperature FLOAT: Generation temperature (0.0-1.0)--dry-run: Show preview without saving--verbose: Show detailed generation info
uitestflow run TEST_FILE [OPTIONS]Options:
--with-claude: Enable guided execution--model MODEL: Claude model for guidance--no-guidance: Disable guidance for this run--guided-only: Show guidance without execution (dry run)
uitestflow mcp-server [OPTIONS]Options:
--port PORT: Server port (default: 3001)--transport TYPE: stdio or http (default: stdio)--headless / --headed: Browser mode
uitestflow mcp-statusShows:
- MCP server status
- Available tools
- Connected browsers
- Configuration
- Health check
import httpx
async def generate_test():
async with httpx.AsyncClient() as client:
response = await client.post(
"http://localhost:8018/api/mcp/generate",
json={
"description": "Login with admin credentials",
"base_url": "http://example.com",
"model": "claude-3-5-haiku-20241022",
"temperature": 0.3,
},
headers={"Authorization": f"Bearer {api_key}"}
)
data = response.json()
test_id = data["test_id"]
test_definition = data["test_definition"]
return test_id, test_definitionasync def execute_test(test_id):
async with httpx.AsyncClient() as client:
response = await client.post(
"http://localhost:8018/api/mcp/execute",
json={
"test_id": test_id,
"with_claude": True,
"guidance_level": "standard",
},
headers={"Authorization": f"Bearer {api_key}"}
)
run_id = response.json()["run_id"]
return run_idimport asyncio
import websockets
async def stream_execution(run_id):
uri = f"ws://localhost:8018/ws/mcp/execute/{run_id}?token={api_key}"
async with websockets.connect(uri) as websocket:
async for message in websocket:
data = json.loads(message)
if data["type"] == "step_started":
print(f"Starting: {data['step_name']}")
elif data["type"] == "claude_guidance":
print(f"Guidance: {data['guidance']}")
elif data["type"] == "step_completed":
print(f"Completed: {data['step_name']} - {data['success']}")
elif data["type"] == "test_completed":
print(f"Test finished: {data['passed']}")
breakasync def get_feedback(run_id):
async with httpx.AsyncClient() as client:
response = await client.get(
f"http://localhost:8018/api/mcp/feedback/{run_id}",
headers={"Authorization": f"Bearer {api_key}"}
)
feedback = response.json()
for step in feedback["steps"]:
print(f"Step: {step['name']}")
if step.get("claude_guidance"):
print(f" Guidance: {step['claude_guidance']}")
if step.get("adapted_selector"):
print(f" Used selector: {step['adapted_selector']}")See .env.mcp.example for complete list. Key variables:
# Enable MCP features
UITESTFLOW_MCP_ENABLED=true
# API key
ANTHROPIC_API_KEY=your-key-here
# Model settings
UITESTFLOW_MCP_CLAUDE_MODEL=claude-3-5-haiku-20241022
UITESTFLOW_MCP_CLAUDE_TEMPERATURE=0.3
# Guidance settings
UITESTFLOW_MCP_GUIDANCE_DETAIL_LEVEL=standard
UITESTFLOW_MCP_GUIDANCE_MAX_RETRIES=3
# Features
UITESTFLOW_MCP_SELECTOR_ADAPTATION_ENABLED=true
UITESTFLOW_MCP_ERROR_RECOVERY_ENABLED=true- CLI arguments (highest priority)
- Environment variables
.envfile- Profile configuration
- Default values (lowest priority)
Example:
# .env sets model to haiku
UITESTFLOW_MCP_CLAUDE_MODEL=claude-3-5-haiku-20241022
# CLI overrides to sonnet
uitestflow generate "test" --model claude-3-5-sonnet-20241022# .uitest_profiles.yaml
profiles:
production:
mcp:
enabled: true
claude_model: claude-3-5-haiku-20241022
guidance_enabled: true
guidance_level: minimalCustomize test generation prompts:
from uitestflow.llm.prompts import TestGenerationPrompt
custom_prompt = TestGenerationPrompt(
system="You are a QA engineer specializing in e-commerce tests...",
user_template="Generate a test for: {description}...",
)
generator = ClaudeTestGenerator(prompt=custom_prompt)Generate multiple tests:
descriptions = [
"Login test",
"Registration test",
"Password reset test",
]
async def batch_generate():
generator = ClaudeTestGenerator()
tasks = [
generator.generate_test(desc)
for desc in descriptions
]
tests = await asyncio.gather(*tasks)
return testsReduce API calls by caching responses:
UITESTFLOW_MCP_CACHE_RESPONSES=true
UITESTFLOW_MCP_CACHE_TTL_SECONDS=3600Control what data is sent to Claude:
# Include console logs (recommended)
UITESTFLOW_MCP_PAGE_STATE_CAPTURE_CONSOLE=true
# Include cookies (may contain sensitive data)
UITESTFLOW_MCP_PAGE_STATE_CAPTURE_COOKIES=false
# Include localStorage (may contain sensitive data)
UITESTFLOW_MCP_PAGE_STATE_CAPTURE_STORAGE=falseProblem: "Invalid API key"
Solution:
# Verify key is set
echo $ANTHROPIC_API_KEY
# Test key directly
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01"Problem: Test generation fails or produces invalid YAML
Solutions:
- Check your description is clear and specific
- Try a different model (Sonnet instead of Haiku)
- Increase timeout:
UITESTFLOW_MCP_CLAUDE_TIMEOUT=120 - Enable verbose logging:
UITESTFLOW_MCP_LOG_LEVEL=DEBUG
Problem: Guidance requests timeout during execution
Solutions:
-
Increase timeout:
UITESTFLOW_MCP_GUIDANCE_TIMEOUT=60 -
Reduce detail level:
UITESTFLOW_MCP_GUIDANCE_DETAIL_LEVEL=minimal -
Disable guidance for specific steps in YAML:
steps: - name: "Fast step" action: "click" selector: "button" llm_config: enabled: false
Problem: Selector adaptation doesn't find alternatives
Solutions:
- Ensure feature is enabled:
UITESTFLOW_MCP_SELECTOR_ADAPTATION_ENABLED=true - Increase alternatives:
UITESTFLOW_MCP_SELECTOR_MAX_ALTERNATIVES=5 - Provide better initial selector (use data-testid attributes)
- Check page state is being captured properly
Problem: "Rate limit exceeded" errors
Solutions:
- Enable response caching:
UITESTFLOW_MCP_CACHE_RESPONSES=true - Reduce guidance frequency (use minimal detail level)
- Batch requests:
UITESTFLOW_MCP_BATCH_REQUESTS=true - Upgrade Anthropic API plan
- Be Specific: Clear descriptions produce better tests
- Use Haiku: Fastest and most cost-effective for most tests
- Include Expected Outcomes: Help Claude understand success criteria
- Mention Data-TestID: If your app uses test IDs, mention them
- Review Generated Tests: Always review before committing
- Start with Standard Level: Adjust based on results
- Use for Complex Tests: Simple tests don't need guidance
- Monitor Costs: Guidance adds API calls
- Enable Caching: Reuse responses for identical scenarios
- Log Everything: Keep logs for debugging
- Prefer Stable Selectors: data-testid > id > class > xpath
- Let Claude Adapt: Don't over-specify selectors
- Review Adaptations: Check what selectors Claude chose
- Update Tests: Use adapted selectors in future versions
- Protect API Keys: Never commit keys to git
- Use Environment Variables: Keep keys in .env
- Limit Page State: Don't send cookies/storage unless needed
- Review Generated Tests: Check for hardcoded secrets
- Rotate Keys: Regularly rotate API keys
- Cache Responses: Enable caching for repeated scenarios
- Use Haiku: Faster and cheaper than Sonnet/Opus
- Batch When Possible: Generate multiple tests together
- Disable for Simple Steps: Skip guidance for trivial actions
- Monitor Usage: Track API calls and costs
- Version Tests: Track when tests were generated
- Update Prompts: Refine prompts based on results
- Monitor Failures: Look for patterns in adapted selectors
- Keep MCP Updated: Update to latest version
- Document Quirks: Note app-specific guidance needs
- GitHub Issues: https://github.com/preset-io/ui-test-flow/issues
- Discussions: https://github.com/preset-io/ui-test-flow/discussions
- Email: [email protected]