Back to Blog

How I Move Faster With Claude Code and a Custom MCP

I was pair programming with Claude Code as its QA. A custom MCP server let it check its own work, and now we ship faster and more accurately.

Sean Robb
6 min read
#ai

Earlier this year I started WorkHoodie, building apps for myself and helping clients ship theirs. Claude Code became my default development partner for both.

I thought I was pair programming with it. I was actually just doing its QA.

Every time it made a change, it would tell me it was done. And it wasn’t. Not maliciously. It just had no way to check its own work. It couldn’t see the app or read the browser console or look at the server logs. So it would confidently declare victory, and I’d go verify, find the issue, describe what I was seeing, paste in the error, and send it back. Then it would fix that and we’d do the whole thing again.

That’s not pair programming. That’s me sitting next to a fast developer who can’t test their own code. The iteration loop was slower and less accurate because everything had to pass through me first, and I’m a lossy middleman between an app and a terminal.

Where I Was Losing Time

The app I was building had three places things could break: the server logs, the browser console, and the visual state of the UI. None of them live in the terminal where Claude Code does.

Before — I was Claude's eyes and ears
Me Claude Code loop I give Claude a task edits code done! (it's not) checks UI, client, server sends screenshots + logs tries a fix done — ready to review

The pattern was always the same. Claude Code would make a change, tell me it was done, and I’d go check. Half the time it wasn’t done. I’d find the issue, describe what I was seeing, paste in the error. It would fix that, tell me it was done again. I’d go check again. Every round trip added time and lost fidelity. My description of a bug was never as good as the actual stack trace. My “the layout looks off” was never as useful as a screenshot.

The obvious response is “just write tests.” Claude Code can run tests. But tests verify known expectations. They tell you the function returns the right value or the API responds with a 200. They don’t tell you the hero image is overlapping the nav on mobile, or that a console error is firing on every page load, or that the layout just feels off in a way you’d catch in two seconds of actually looking at it. Tests cover what you already know to check for. The gap is everything else.

The Fix: A Custom MCP Server

MCP lets you give Claude Code custom tools it can call on its own. So I built a small MCP server that gives it eyes and ears into my local dev environment. Enough for it to check its own work before coming to me. Five TypeScript files, nothing fancy:

tools/dev-server-mcp/
├── index.ts            # MCP server + tool definitions
├── server-manager.ts   # start, stop, logs
├── screenshot.ts       # screenshots + visual diff
├── perf.ts             # Core Web Vitals
└── types.ts            # shared types

Claude Code discovers it from a .mcp.json file in the project root:

{
  "mcpServers": {
    "engage-dev-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "tools/dev-server-mcp/index.ts"]
    }
  }
}

If Claude Code can see the app, read the logs, and take screenshots, it can do its own QA. The whole iteration loop gets faster and more accurate because the machine is reading its own stack traces instead of my descriptions of them.

It can start and stop the dev server, read server logs, take screenshots across device viewports, capture browser console output, run visual diffs against a baseline, and measure Core Web Vitals. Everything I’d normally check by hand.

What Actually Changed

After — MCP is Claude's eyes and ears
Me Claude Code loop I give Claude a task edits code MCP checks UI, client, server screenshots, fixes, verifies done — ready to review

The first time Claude Code started the server, took a screenshot, noticed a layout issue, fixed it, took another screenshot, and confirmed the fix without me touching anything, I just sat there. The whole loop ran without me.

Before, I was the bottleneck in that iteration loop. Checking the browser, reading logs, describing errors. That work matters, but I was adding latency and losing fidelity every time I relayed what I was seeing. The MCP gives Claude Code direct access to the signal, so the loop runs faster and catches more.

And because the loop is tighter, I have time I didn’t before. The questions I’m asking changed. Whether the thing we’re building actually solves the problem we scoped. Whether I should redirect before Claude goes three rounds deep on the wrong approach. I wasn’t getting to that stuff when I was busy relaying screenshots.

Build Your Own

Start with whatever feedback loop costs you the most time. Mine was “make change, check browser, describe result.” Yours might be checking test output or verifying API responses or looking at database state.

Paste this prompt into Claude Code from your project root and it will inspect your setup and build a custom MCP server tailored to your stack.

I want you to build a dev server MCP for this project so you can control my local dev environment directly. This removes the copy-paste bottleneck where I have to relay logs, errors, and screenshots between you and my running app. First, read the project's package.json (and any framework config files like vite.config.*, next.config.*, astro.config.*, etc.) to understand the dev server setup — what command starts it, what port it runs on, and what framework is in use. Then build an MCP server in a tools/dev-server-mcp/ directory with these tools:
I want you to build a dev server MCP for this project so you can control my local dev environment directly. This removes the copy-paste bottleneck where I have to relay logs, errors, and screenshots between you and my running app. First, read the project's package.json (and any framework config files like vite.config.*, next.config.*, astro.config.*, etc.) to understand the dev server setup — what command starts it, what port it runs on, and what framework is in use. Then build an MCP server in a tools/dev-server-mcp/ directory with these tools: 1. dev_server_start — Start the dev server as a background process. Write the PID to a state file so we can track it. Poll until the server responds on its port before returning. Use the project's actual dev command. 2. dev_server_stop — Kill the running dev server process using the stored PID. Clean up state files. 3. dev_server_status — Report whether the server is running, what port, and the PID. Clean up stale state if the process died. 4. dev_server_logs — Read the last N lines of server output. Support a substring filter parameter. The server's stdout/stderr should be piped to a log file when started. 5. dev_server_screenshot — Use Playwright to load a URL path on the running server in headless Chromium and return a PNG image. Support device presets (desktop 1280x720, mobile 390x844, tablet 820x1180), full-page capture, and a configurable wait time for animations. Scroll through the page before capture to trigger any scroll-based animations. 6. dev_server_console_logs — Use Playwright to load a page and capture all browser console output (logs, warnings, errors). Filter out dev noise (HMR, React DevTools, Vite connection messages) by default. 7. dev_server_screenshot_diff — Accept a baseline PNG file path, take a new screenshot of a given path, and compare them with pixelmatch. Return before, after, and diff images with the percentage of changed pixels. 8. dev_server_perf — Measure Core Web Vitals (FCP, LCP, CLS, TBT) using Chrome Performance APIs via Playwright. Run multiple times and report medians. Include diagnostics that flag metrics outside standard thresholds. Implementation requirements: - Use @modelcontextprotocol/sdk for the MCP server - Use Playwright for all browser automation - Use zod for input validation on all tools - Use pngjs and pixelmatch for visual diffing - Manage state with flat files (PID, port, mode, log) — no database - TypeScript, runnable with tsx - Keep it to as few files as practical After building the server, register it in .mcp.json at the project root so Claude Code auto-discovers it. The command should be npx tsx tools/dev-server-mcp/index.ts. Install any missing dependencies (playwright, pixelmatch, pngjs, @modelcontextprotocol/sdk, zod) and run npx playwright install chromium.