DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

Playwright MCP Is Great for Automation. PageBolt MCP Is How You Audit It.

Playwright MCP Is Great for Automation. PageBolt MCP Is How You Audit It.

You're building AI agents with Playwright MCP. It handles browser automation beautifully—click, fill forms, navigate, extract data. But when your auditor asks "Show me what the agent actually did," you have API logs. You don't have visual proof.

That's where PageBolt MCP comes in. While Playwright MCP executes actions, PageBolt MCP captures screenshots. Together, they form a compliance-grade audit trail: execution + visual evidence.

The Problem: Execution Without Proof

Playwright MCP is designed for one thing: making browser automation accessible to AI agents. It's powerful. Your agent can:

  • Navigate to a URL
  • Fill form fields
  • Click buttons
  • Wait for elements
  • Extract text/data

But here's the gap: you can't prove to regulators what happened.

Scenario 1: Compliance audit
Your agent filled out a form and submitted it. API logs show "button_clicked: submit". But did the form actually submit? Did an error message appear that the agent missed? Your auditor can't tell from logs alone.

Scenario 2: Silent failure
The page loaded. The agent extracted data. But what if the page rendered an error message? What if the data was partial? Logs don't show rendering failures.

Scenario 3: Lateral movement risk
Your agent logs show normal API calls. But did it access systems outside its intended scope? Did it trigger compliance violations? Visual proof answers this.

The Solution: Playwright MCP + PageBolt MCP

Combine them:

  1. Playwright MCP executes the automation (navigate, click, fill)
  2. PageBolt MCP captures screenshots at decision points (before/after actions, errors, data extraction)
  3. Your system stores the audit trail (timestamp + screenshot + action)

Real Workflow: Form Submission with Audit Proof

Here's production code using both MCPs together:

const Anthropic = require("@anthropic-ai/sdk");

const client = new Anthropic();

// Define both MCPs as tools
const tools = [
  {
    name: "playwright_navigate",
    description: "Navigate to a URL using Playwright",
    input_schema: {
      type: "object",
      properties: {
        url: { type: "string" }
      },
      required: ["url"]
    }
  },
  {
    name: "playwright_fill",
    description: "Fill a form field",
    input_schema: {
      type: "object",
      properties: {
        selector: { type: "string" },
        value: { type: "string" }
      },
      required: ["selector", "value"]
    }
  },
  {
    name: "playwright_click",
    description: "Click an element",
    input_schema: {
      type: "object",
      properties: {
        selector: { type: "string" }
      },
      required: ["selector"]
    }
  },
  {
    name: "pagebolt_screenshot",
    description: "Take a screenshot for audit proof",
    input_schema: {
      type: "object",
      properties: {
        url: { type: "string" },
        purpose: { type: "string", description: "Why we're capturing this (e.g., 'before submission', 'error check')" }
      },
      required: ["url", "purpose"]
    }
  }
];

// Audit trail storage
const auditTrail = [];

// Agent with audit loop
async function auditableAgent(task) {
  const messages = [
    {
      role: "user",
      content: `${task}\n\nIMPORTANT: Before and after each action, take a screenshot with PageBolt. This is your audit proof for compliance. Include the purpose (e.g., "before form fill", "after submission", "error check").`
    }
  ];

  let response = await client.messages.create({
    model: "claude-opus-4-5-20251101",
    max_tokens: 4096,
    tools: tools,
    messages: messages
  });

  while (response.stop_reason === "tool_use") {
    const toolUse = response.content.find(block => block.type === "tool_use");

    // Handle Playwright actions
    if (toolUse.name.startsWith("playwright_")) {
      console.log(`Playwright: ${toolUse.name}${JSON.stringify(toolUse.input)}`);

      // In real implementation, call Playwright MCP here
      // For demo: simulate success
      const result = `Success: ${toolUse.name} executed`;

      messages.push({ role: "assistant", content: response.content });
      messages.push({
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: toolUse.id,
            content: result
          }
        ]
      });
    }

    // Handle PageBolt screenshots
    if (toolUse.name === "pagebolt_screenshot") {
      console.log(`PageBolt: Capturing screenshot for audit (${toolUse.input.purpose})`);

      // Call PageBolt API
      const screenshot = await fetch("https://api.pagebolt.com/screenshot", {
        method: "GET",
        headers: {
          "Authorization": `Bearer ${process.env.PAGEBOLT_API_KEY}`
        },
        params: { url: toolUse.input.url }
      }).then(r => r.json()).catch(e => ({ error: e.message }));

      // Store in audit trail
      auditTrail.push({
        timestamp: new Date().toISOString(),
        action: "screenshot",
        url: toolUse.input.url,
        purpose: toolUse.input.purpose,
        screenshot_id: screenshot.id || null
      });

      messages.push({ role: "assistant", content: response.content });
      messages.push({
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: toolUse.id,
            content: `Screenshot captured: ${screenshot.id}`
          }
        ]
      });
    }

    // Continue agent loop
    response = await client.messages.create({
      model: "claude-opus-4-5-20251101",
      max_tokens: 4096,
      tools: tools,
      messages: messages
    });
  }

  return {
    result: response.content,
    auditTrail: auditTrail
  };
}

// Run it
const result = await auditableAgent("Fill out the signup form at https://app.example.com/signup with name='John Doe', email='john@example.com'. Submit and verify success.");
console.log("Audit Trail:", JSON.stringify(result.auditTrail, null, 2));
Enter fullscreen mode Exit fullscreen mode

What this does:

  • Agent takes a screenshot before filling the form (captures initial state)
  • Fills fields using Playwright MCP
  • Takes a screenshot after filling (captures form state with data)
  • Clicks submit using Playwright MCP
  • Takes a screenshot after submit (captures success/error page)
  • Returns complete audit trail with timestamps and screenshots

Your auditor can replay the entire session as a visual timeline.

Compliance Use Cases

SOC 2 / HIPAA:
Screenshot healthcare records access → Proves agent accessed only authorized data.

GDPR:
Screenshot consent forms → Proves agent respected user preferences.

PCI DSS:
Screenshot payment pages → Proves agent never captured card details.

FedRAMP:
Screenshot access logs → Proves agent behavior matches security policy.

Without visual proof, you have API logs. With it, you have irrefutable evidence.

When to Use PageBolt + Playwright

Scenario Playwright Alone + PageBolt MCP
Test automation (internal) ✅ Sufficient ❌ Overkill
Customer-facing automation ❌ No audit trail ✅ Compliance proof
Regulated industry access ❌ Risky ✅ Required
Agent debugging ⚠️ Logs only ✅ Visual debugging
Legal/audit response ❌ Insufficient ✅ Irrefutable

Rule: If your agent touches regulated data or customer-facing systems, add PageBolt screenshots.

Cost Comparison

Self-hosted visual audit:

  • Puppeteer screenshot server: $500+/month
  • Storage (videos/images): $100+/month
  • Ops time: 20+ hours/month

PageBolt MCP:

  • API cost: $29/month (10k screenshots)
  • Storage: Included
  • Ops time: 0 hours/month

At scale, PageBolt is 10-20x cheaper than self-hosted solutions.

Next Steps

  1. Add PageBolt MCP to your Claude Agent SDK setup
  2. Modify Playwright agents to capture screenshots at decision points
  3. Store audit trail in your compliance system
  4. Show your auditor the visual replay

Start free — 100 screenshots/month, no credit card. Add compliance proof to your next Playwright automation.

Top comments (0)