DEV Community

Custodia-Admin
Custodia-Admin

Posted on • Originally published at pagebolt.dev

I Added Screenshot Proof to Every Windsurf MCP Tool Call

I Added Screenshot Proof to Every Windsurf MCP Tool Call

I've been using Windsurf as my primary IDE for three months. Last week I built my first MCP tools for it. By week two, I realized the same problem I had with other tools: I have no idea what's actually happening.

Windsurf calls my tools. The tools run browser automation. Windsurf gets back JSON. But I don't see the page. I don't know if the action worked. I'm debugging blind.

So I added PageBolt screenshots to every tool call.

Same thing I did with Cursor. But Windsurf's MCP integration is slightly different, and the debugging workflow is different. Here's what I learned.

The Problem: Windsurf Tools Are Black Boxes

Windsurf's strength is that it understands your full codebase context. You can ask it to "integrate this API", "add authentication", "automate this workflow", and it does it. MCP tools extend that: you can build custom tools that Windsurf calls natively.

But when Windsurf calls your MCP tool, you see the output, not the execution:

  1. You ask Windsurf: "Scrape the pricing page and extract plan details"
  2. Windsurf calls your scrape_pricing MCP tool
  3. Tool runs (navigates, scrapes, returns JSON)
  4. Windsurf reads the JSON and continues
  5. You see: { success: true, plans: 3, captured: 2026-03-22 }
  6. You have no idea if the scraping actually worked

Windsurf can't see the page. I can't see the page. Only my tool knows the truth.

Why Windsurf Developers Need This Even More

Windsurf's selling point is agentic flow — it works autonomously, making decisions and calling tools without asking. That's powerful. That's also terrifying when you can't see what it's doing.

Screenshot proof is the safety net.

How I Integrated It

Windsurf's MCP integration expects tools to be exported from a Node.js module. Here's my pattern:

// mcp-tools.js
import fetch from 'node-fetch';
import fs from 'fs';

const PAGEBOLT_API_KEY = process.env.PAGEBOLT_API_KEY;

// Helper: capture proof after action
async function captureProof(actionName, pageUrl) {
  try {
    const response = await fetch('https://api.pagebolt.dev/v1/screenshot', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${PAGEBOLT_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        url: pageUrl,
        format: 'png',
        width: 1280,
        height: 720
      })
    });

    if (!response.ok) {
      console.error(`Screenshot failed: ${response.status}`);
      return null;
    }

    const buffer = await response.arrayBuffer();
    const filename = `${actionName}-${Date.now()}.png`;
    fs.writeFileSync(filename, Buffer.from(buffer));
    return filename;
  } catch (error) {
    console.error(`Proof capture error: ${error.message}`);
    return null;
  }
}

// Tool exported to Windsurf
export const scrapePricingPage = {
  description: 'Scrape pricing details from a website',
  inputSchema: {
    type: 'object',
    properties: {
      url: {
        type: 'string',
        description: 'URL of the pricing page'
      }
    },
    required: ['url']
  },
  handler: async (input) => {
    const { url } = input;

    // Your automation logic here
    // (Puppeteer, Playwright, etc.)
    const plans = await scrapePricingLogic(url);

    // Capture proof
    const proofFile = await captureProof('scrape-pricing', url);

    return {
      success: true,
      plans: plans.length,
      details: plans,
      visual_proof: proofFile,
      message: proofFile
        ? `Scraped ${plans.length} plans. Proof: ${proofFile}`
        : `Scraped ${plans.length} plans (screenshot failed)`
    };
  }
};

export const fillFormWithProof = {
  description: 'Fill a web form and capture proof of submission',
  inputSchema: {
    type: 'object',
    properties: {
      url: { type: 'string' },
      formData: {
        type: 'object',
        description: 'Form field-value pairs'
      }
    },
    required: ['url', 'formData']
  },
  handler: async (input) => {
    const { url, formData } = input;

    // Fill form (your automation)
    await fillFormLogic(url, formData);

    // Capture proof of completed form
    const proofFile = await captureProof('form-filled', url);

    return {
      success: true,
      filled_fields: Object.keys(formData),
      visual_proof: proofFile,
      message: proofFile ? `Form filled. See ${proofFile}` : `Form filled`
    };
  }
};
Enter fullscreen mode Exit fullscreen mode

Windsurf sees the returned JSON. You see the proof files.

The Debugging Advantage

This is where Windsurf's agentic flow becomes safer. Because:

  1. Windsurf calls tool → gets result + proof file
  2. You can review proof at your own pace
  3. If something breaks, you have visual evidence
  4. Next iteration, Windsurf can call the tool again (with fixes)

I had a tool that was "failing" according to Windsurf. The output said { success: false, error: "Timeout" }.

Normally I'd dig through logs. But the proof file showed: the page loaded fine, the form appeared, but a modal popup appeared on top of it, blocking the form.

My timeout wasn't a timeout. It was an unhandled popup. The proof showed me in one glance. Fixed it in 30 seconds.

Real Workflow: Using This With Windsurf

  1. Ask Windsurf: "Automate signup on example.com"
  2. Windsurf creates/calls: your MCP tool
  3. Tool runs: navigates, fills form, submits, captures proof
  4. Windsurf sees: { success: true, submitted: true, visual_proof: "proof.png" }
  5. You review: the proof file, verify the action actually worked
  6. If broken: show Windsurf the proof file, ask it to fix the tool

The proof becomes part of the loop.

Why This Matters for Agentic IDEs

Cursor, Windsurf, and other agentic tools work by:

  • Understanding your code context
  • Taking autonomous actions (write, build, deploy)
  • Using tools for external actions (browser, API, etc.)

But when tools are black boxes, agentic flow becomes fragile. You have to trust the output.

Visual proof changes that. You can trust because you can see.

Integration for Windsurf (Step by Step)

  1. Create MCP tool file:
mkdir mcp-tools
touch mcp-tools/index.js
Enter fullscreen mode Exit fullscreen mode
  1. Add PageBolt proof capture:
// Copy the captureProof helper above
Enter fullscreen mode Exit fullscreen mode
  1. Export your tools:
export const myTool = { ... }
Enter fullscreen mode Exit fullscreen mode
  1. Configure in Windsurf settings:
    Add your MCP tool path to Windsurf's MCP configuration

  2. Use from Windsurf:
    Ask Windsurf to call your tool, review the proof

Pricing

Plan Requests/Month Cost Best For
Free 100 $0 Development & testing
Starter 5,000 $29 Small tools & projects
Growth 25,000 $79 Production automation
Scale 100,000 $199 Enterprise agentic flows

Summary

  • ✅ Windsurf MCP tools are powerful but invisible
  • ✅ Screenshot proof gives you visibility
  • ✅ One API call after each tool action
  • ✅ Review proof to verify autonomous actions worked
  • ✅ Safer, faster debugging

Get started: PageBolt free — 100 requests/month, no credit card →

Top comments (0)