Modern QA2026Hybrid Strategies: Using Skills and MCP Together
Log inJoin

Course01 Agent Skills for Browser Automation⊞ Tile viewNew!

Cutting-edge · Chapter 01

Hybrid Strategies: Using Skills and MCP Together

Updated Aug 2026

Why Hybrid?

Skills and MCP each have strengths the other lacks:

Capability Skills (Playwright CLI) MCP
Token efficiency Excellent (~4x cheaper per task, measured) Poor
Semantic page understanding YAML accessibility snapshots — read on demand Accessibility trees — streamed always
Speed per step Dozens of tokens per command Thousands of tokens per action
Works without a filesystem No — disk is the whole trick Yes — inline state
Discovery snapshot + read the YAML Automatic (tree arrives with every action)

The 2026 division of labor is simpler than it was in the vibe-check era: the CLI now carries semantic page understanding too (same accessibility data, on disk). What MCP retains is environments where there is no disk. A hybrid approach uses the CLI wherever the agent has a filesystem and MCP where it doesn't — or where an integration is MCP-native.

Strategy 1: Snapshot for Discovery, Refs for Execution

The Pattern

The discovery/execution split now lives inside the CLI:

Phase 1: Discovery (one snapshot read)
  Agent: "What's on this page?"
  → playwright-cli snapshot        (returns a file path)
  → Agent reads the YAML: textbox "Email" [ref=e3],
    textbox "Password" [ref=e4], button "Sign in" [ref=e5]

Phase 2: Execution (refs, dozens of tokens each)
  → playwright-cli fill e3 "user@test.com"
  → playwright-cli fill e4 "secret"
  → playwright-cli click e5

Token Cost

Discovery phase:  ~1,500-5,000 tokens (one snapshot read, sized by page)
Execution phase:  ~200 tokens (3 ref commands)
Total:            ~2,000-5,000 tokens

vs. Pure MCP:     ~15,000-25,000 tokens (schemas every turn + a tree per action)

When to Use

  • Always, basically — this is the default working rhythm of a CLI-driven agent
  • UI that changes frequently — a fresh snapshot re-discovers refs automatically
  • First test run of a new feature — later runs already know the flow

The equivalent MCP pattern (call browser_snapshot, then act) still applies when you're on the MCP transport — the difference is that with MCP you also pay the tree tax on every action, not just on discovery.

Strategy 2: CLI by Default, Re-snapshot as Fallback, MCP for No-Disk Environments

The Pattern

# Pseudocode for the agent's behavior
try:
    # Primary: act on a known ref (fast, cheap)
    playwright-cli click e5
except StaleOrMissingRef:
    # Fallback: refresh understanding from disk
    playwright-cli snapshot
    # Agent reads the new YAML, finds "Sign in" button's new ref
    playwright-cli click e9

# Separately: if this flow must also run from a sandboxed surface
# with no filesystem, route that environment through Playwright MCP.

Token Cost

Happy path (ref works):        ~55 tokens
Fallback path (re-snapshot):   ~2,000-5,000 tokens (one snapshot read + retry)
Average (assuming 90% success): a few hundred tokens per step

When to Use

  • Mature test suites where most flows are stable
  • Self-healing tests that adapt when the UI changes (see the Self-Healing Strategies chapter — Playwright's healer agent productizes exactly this loop)
  • Cost optimization — pay for page state only when something breaks

Strategy 3: Different Tools for Different Test Types

The Pattern

Test Type Primary Tool Why
Functional tests CLI (playwright-cli) Known flows, ref-driven, fast
Accessibility audits CLI snapshot (YAML a11y tree) The snapshot is an accessibility summary — grep it, reason over it
Visual regression CLI + screenshot Files on disk, compared without entering context
Exploratory testing CLI snapshot + agent reasoning Discovery is a read away
Sandboxed / hosted agents Playwright MCP No filesystem — inline state is the only option
API integration checks CLI + eval playwright-cli eval for XHR/state inspection
Performance checks CLI + eval playwright-cli eval "JSON.stringify(performance.timing)"

Configuration

# test-framework-config.yaml
test_types:
  functional:
    tool: playwright-cli
    session: per-test          # fresh named session per test
    headed: false

  accessibility:
    tool: playwright-cli
    artifacts: snapshots       # keep YAML snapshots for audit

  visual:
    tool: playwright-cli
    screenshots: true
    comparison_threshold: 0.98

  hosted_exploratory:
    tool: playwright-mcp       # sandboxed surface, no filesystem
    snapshot_mode: full

Strategy 4: Phased Framework Adoption

Phase 1: CLI + Skill Only (Week 1-2)

Start with the simplest approach:

npm install -g @playwright/cli@latest
playwright-cli install            # workspace: .playwright-cli/
playwright-cli install --skills   # SKILL.md for your coding agent

Write your first flows as snapshot-then-refs sequences:

# Simple test flow
playwright-cli open https://app.example.com/login
playwright-cli snapshot                     # read YAML → refs
playwright-cli fill e3 "test@example.com"
playwright-cli fill e4 "password"
playwright-cli click e5
playwright-cli snapshot                     # verify: "Welcome" heading present

Phase 2: Add MCP Where the CLI Can't Go (Week 3-4)

When flows must also run from MCP-native or sandboxed surfaces:

  • Add Playwright MCP: claude mcp add playwright -- npx @playwright/mcp@latest (it's published to the official MCP Registry each release)
  • Route only those environments through it; keep everything with a filesystem on the CLI
  • Since Playwright 1.59, browser.bind() can even point both transports at one shared browser

Phase 3: Intelligent Routing (Week 5+)

Build routing logic that chooses the right transport:

  • Agent has a filesystem → CLI, always
  • Sandboxed surface → MCP
  • CI runs → CLI only (cost optimization), traces archived
  • Failures → re-snapshot first; escalate to the healer agent workflow

Implementation: A Simple Hybrid Test

Here's what the working rhythm looks like in practice, as an agent would execute it:

Agent thinking: "I need to test the login flow on the redesigned page.
I don't know the new structure. Snapshot first."

Step 1: Navigate (cheap)
  → Bash: playwright-cli open https://app.example.com/login

Step 2: Discover page structure (one disk read)
  → Bash: playwright-cli snapshot     → .playwright-cli/ YAML path
  → Agent reads:
    - form "Login":
      - textbox "Email address" [ref=e3]
      - textbox "Password" [ref=e4]
      - button "Sign in" [ref=e5]
      - link "Forgot password?" [ref=e6]

Step 3: Execute test (refs — cheap)
  → Bash: playwright-cli fill e3 "test@example.com"
  → Bash: playwright-cli fill e4 "secret"
  → Bash: playwright-cli click e5

Step 4: Verify (one more snapshot read)
  → Bash: playwright-cli snapshot
  → Agent reads: heading "Dashboard", text "Welcome, Test User"

Step 5: Evidence (file path only — image never enters context)
  → Bash: playwright-cli screenshot

Total: 2 snapshot reads (~4K tokens) + 6 commands (~400 tokens) ≈ 4,500 tokens
vs. Pure MCP for the same flow: ~25,000-35,000 tokens

Anti-Patterns to Avoid

Don't: Use MCP for Every Click When You Have a Filesystem

# BAD: Each action via MCP from inside Claude Code
browser_click(ref=e5)      # tree streamed with the response
browser_type(ref=e3, "hi") # tree streamed again
browser_click(ref=e7)      # and again
# Schemas every turn + a tree per action, for no benefit

Don't: Act on Guessed or Stale Refs

# BAD: refs from a page that has since changed
playwright-cli click e5      # ref belonged to the old snapshot
playwright-cli click e12     # guessing
# Wasted attempts + error handling

# GOOD: refs always come from a current snapshot
playwright-cli snapshot      # refresh
# read YAML → button "Submit" [ref=e9]
playwright-cli click e9

Do: Match the Transport to the Environment

# GOOD
Filesystem available (Claude Code, CI)  → playwright-cli + SKILL.md
No filesystem (hosted/sandboxed agent)  → Playwright MCP
Both needed on one browser              → browser.bind() shares the instance

Interview Talking Point

"Our framework treats skills and MCP as transports, not religions — which is Microsoft's own posture, since they ship both and recommend the CLI for coding agents at about 4x fewer tokens. The default is the Playwright CLI: snapshot once to discover the page as YAML on disk, then act by element refs at a few dozen tokens per command; when a ref goes stale we re-snapshot rather than guess. Playwright MCP stays in the stack for exactly one reason — surfaces without filesystem access, where inline state is the only option. And because browser.bind() lets both transports share one browser instance, routing per environment costs us nothing architecturally."