Skip to content
All posts
·6 min read·Shashank Bindal

Giving Claude Code, Cursor and Other Agents a Verifier: The quelltest MCP Server and a Stop Hook

Run npx quelltest mcp to give any MCP client find, verify and gate tools, and add a Claude Code Stop hook that blocks done when tests were weakened.

TL;DR. npx -y quelltest mcp starts an MCP server over stdio with three tools: find, verify and gate. Register it in Claude Code with one claude mcp add line, and add a Stop hook that runs quell gate and exits 2 when tests were weakened, which tells Claude Code to keep working. We checked the server with a scripted stdio client; we have not run the hook inside a live Claude Code session.

This post is about the npm package (JavaScript and TypeScript projects). The Python package has a different MCP server with different tools (and its newest version is ahead of the PyPI release), described in /docs/mcp and the earlier agent-native post.

Why give an agent a verifier instead of a prompt?

An agent that writes the code also writes the tests that judge it, so the most common failure is a suite that goes green because it was edited, not because the code is right. Asking the agent to "be rigorous" is a request. A tool that returns a failing exit code is a check.

verify and gate are those checks, and MCP lets the agent call them as tools inside its own loop. verify returns surviving mutants as a handoff the agent can act on by strengthening the test.

How do I add the quelltest MCP server to Claude Code?

claude mcp add quelltest -- npx -y quelltest mcp

The -- separates Claude Code's own options from the command that starts the server. That is the documented shape of claude mcp add [options] <name> -- <command> [args...].

Any other MCP client can launch the same command. Most clients take a JSON entry with a command and arguments, along these lines. Check your client's MCP documentation for the exact file and keys:

{
  "mcpServers": {
    "quelltest": { "command": "npx", "args": ["-y", "quelltest", "mcp"] }
  }
}

For Cursor, start from Cursor's documentation. We did not test Cursor, Windsurf or any client other than our own scripted one.

Does npx quell mcp really answer tools/list?

We did not want to claim it without checking. This script spawns the server, performs the MCP handshake over newline-delimited JSON-RPC, and asks for the tool list:

import { spawn } from "node:child_process";

const child = spawn("npx", ["-y", "quelltest", "mcp"], { stdio: ["pipe", "pipe", "inherit"], shell: true });
const send = (message) => child.stdin.write(JSON.stringify(message) + "\n");

child.stdout.on("data", (chunk) => {
  for (const line of chunk.toString().split("\n").filter(Boolean)) {
    const message = JSON.parse(line);
    if (message.id === 1) {
      console.log("initialize ->", JSON.stringify(message.result));
      send({ jsonrpc: "2.0", method: "notifications/initialized" });
      send({ jsonrpc: "2.0", id: 2, method: "tools/list" });
    }
    if (message.id === 2) {
      console.log("tools/list ->", message.result.tools.map((tool) => tool.name).join(", "));
      child.kill();
    }
  }
});

send({ jsonrpc: "2.0", id: 1, method: "initialize", params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "check", version: "0" } } });

Output, from the published quelltest@0.3.1:

initialize -> {"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"quelltest","version":"0.3.1"}}
tools/list -> find, verify, gate

The input schemas we read from the same response:

ToolRequired argumentsOther arguments
findnonepath, diff, coverage, top
verifytest, sourcefunctions, runner, min_score
gatediffstrict

find lists risk-ranked edge-case candidates and executes nothing. verify runs the test on the original source and then on mutants. gate reports deleted, skipped, .only, removed and loosened tests. The behavior of each is the same as the command line, which is described in the verify post and the gate post.

One detail matters for agents on Jest, Vitest or Mocha: the verify tool takes a runner argument, and the default is node (the node:test runner). See the runner post.

How do I block "done" with a Claude Code Stop hook?

Claude Code runs a Stop hook when the agent finishes responding. Exit code 2 blocks the stop, and the hook's stderr goes back to the agent as the reason. Add this to .claude/settings.json:

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          { "type": "command", "command": "npx quell gate --diff origin/main || exit 2" }
        ]
      }
    ]
  }
}

quell gate exits with code 5 when it finds weakened tests. The || exit 2 turns that into the blocking code. The same line works in Windows cmd, which supports both || and exit 2. Exit code 1, which a plain failure often gives, is not a blocking code for hooks, so do not drop the || exit 2.

To also require that the tests you touched can fail, add a verify step for the test file that changed:

npx quell verify tests/pay.test.js --source src/pay.js --min-score 60 --handoff quell-handoff.md || exit 2

The --handoff file lists each surviving mutant with the diff, the surrounding code and a specific instruction. An agent can open it and fix the test.

What should I watch out for?

  • A hook that always blocks can trap a session. If the agent cannot satisfy the gate, it will keep being told no. When a weakening is deliberate, the agent can add // quell-allow-weaken <reason> in the test, which is visible in the diff. Try the hook on a scratch branch first.
  • The gate compares against a ref. origin/main must exist locally, so fetch before running.
  • The verify hook runs your tests once per mutant. Keep it to the files that changed.

What are the limits?

  • The hook and MCP recipes follow the documented Claude Code conventions but have only been exercised through quelltest's unit tests and a scripted stdio client, not inside a live Claude Code session. Treat them as a starting point. That is also the caveat in the project's own docs/agents.md.
  • The server reports on the paths and refs it is given. It is a mechanical check, not a substitute for human review of what the agent changed.
  • The gate does not catch a test rewritten under a new name, weakening done by editing the source, or logic inside test helpers. See limits.
  • An agent can still ignore a tool it is not forced to call. The Stop hook is what makes the check mandatory.

More recipes, including pull-request workflows, are in /docs/npm/agents and /docs/npm/ci. Version history is in the changelog.

FAQ

Does the MCP server send my code to a service?

No. It runs on your machine and quelltest sends no code to any service. Your MCP client and your model provider have their own data handling, which quelltest does not control.

Which Node.js version does it need?

The published 0.3.1 declares Node.js 20 or newer. The next release will require Node.js 22.18+ or 24.11+.

Can the agent write tests through it?

Not in 0.3.1. The MCP server exposes find, verify and gate. Test generation is the CLI command quell generate.

How is this different from the Python MCP server?

The Python repository has a quelltest-mcp server with tools for finding gaps and writing verified pytest tests. As of 2026-10-20 that command is in the repository (version 2.0.1) and not in the published PyPI 2.0.0 wheel, which ships an earlier server started with python -m quell.mcp_server. The npm server in this post is a separate implementation with the three tools above.

Sources