npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

verify-gate-mcp

v0.1.0

Published

MCP server enforcing prover≠verifier task integrity: tasks are claimed done by one agent and can only be verified by a different agent.

Readme

verify-gate-mcp

An MCP server that enforces one rule: the agent that does the work can never mark it complete. Tasks are created with acceptance criteria, claimed done by one agent with evidence, and can only be verified by a different agent. Terminal states are irreversible.

If you run a fleet of AI agents, you have probably watched one report "done, all tests passing" on work that was never finished. This server makes that report structurally impossible to act on until a second agent has checked it.

Why prover and verifier must be separate

An agent asked to both do a task and judge whether the task is done has a conflict of interest baked into its context. It knows what it intended, it remembers writing the fix, and "the work I just described is complete" is the most probable next token. It is not lying; it is pattern-completing. The result is self-signoff: a confident completion claim with nothing behind it.

Separation fixes this the same way it does in human engineering orgs. The prover produces evidence. A verifier who did not do the work, and has no memory of doing it, checks that evidence against the actual system. The verifier's context contains the acceptance criteria and the running code, not the prover's good intentions.

Three catches from running this gate in production, before open-sourcing it:

  1. A devops agent reported a "fix" for a P0 incident. The verify gate found no fix was actually running.
  2. A trading gate had a fail-OPEN bug (the system would trade when it should have halted). Verification caught it before any live capital was exposed.
  3. Work that is actually done passes clean on the first try. The gate converges; it is a filter, not a wall.

All three came from the same rule: prover and verifier must be different agents, and the gate lives server-side, backed by a SQLite CHECK constraint, not in a prompt. The failure mode this guards against is honest-but-lazy agents: an agent marking its own work as verified out of convenience or context-blur.

Invariants

  1. Criteria at birth: a task cannot exist without at least one non-empty acceptance criterion; there is no add-criteria-later tool.
  2. Prover recorded: claim_done permanently records the claiming agent and evidence.
  3. Prover ≠ verifier: verify hard-rejects when the verifier is the claimer (E_SELF_VERIFY), for both verdicts. The same rule is also a database CHECK constraint.
  4. Forward-only: pending → claimed → (verified | rejected). Both end states are terminal; a rejected task's remediation is a new task with supersedes set for lineage.
create_task → pending → claim_done(A) → claimed → verify(B≠A) → verified | rejected
                                          │
                                          └ verify(A) → E_SELF_VERIFY (state unchanged)

Install

Requires Node.js 20+ and any MCP-capable agent client; Claude Code is the tested reference client.

npm install -g verify-gate-mcp

Or run it without installing via npx, directly from your MCP client config:

{
  "mcpServers": {
    "verify-gate": {
      "command": "npx",
      "args": ["-y", "verify-gate-mcp", "--db", "/shared/tasks.db"],
      "env": { "VG_AGENT_ID": "devops" }
    }
  }
}

From source:

git clone https://github.com/Monkeyattack/verify-gate-mcp.git
cd verify-gate-mcp
npm install && npm run build
node dist/index.js --db ./tasks.db

Flags: --db <path> sets the SQLite database path (default ./verify-gate.db, env VG_DB; use :memory: for an ephemeral demo). --agent-id <id> (env VG_AGENT_ID) locks the connection to one identity.

Multiple agents each run their own stdio server process pointed at the same SQLite file (WAL mode); every state transition is a compare-and-swap inside an IMMEDIATE transaction, so concurrent claims have exactly one winner.

Quickstart: first verified task in under 15 minutes

You need two agent identities. The simplest setup is two Claude Code sessions pointed at the same database file, one launched with VG_AGENT_ID=devops, the other with VG_AGENT_ID=reviewer.

Step 1: create a task with checkable criteria

Criteria must be checkable by someone who did not do the work. "Endpoint returns 200" is checkable; "works well" is not. If the verifier cannot decide pass or fail from the criterion alone, rewrite it.

create_task({
  "title": "Add /healthz endpoint to the api service",
  "acceptance_criteria": [
    "GET /healthz on the running service returns HTTP 200",
    "Response body is JSON containing {\"status\": \"ok\"}",
    "npm test passes with the new healthz test included"
  ],
  "created_by": "devops"
})

Returns { "task_id": "…", "status": "pending", "created_at": "…" }.

Step 2: worker claims done, with evidence

The worker (here, devops) does the work, then submits evidence: what was done and how each criterion is satisfied. Evidence is a claim to be tested, not a status flag.

claim_done({
  "task_id": "<task_id from step 1>",
  "agent_id": "devops",
  "evidence": "Added healthz route in src/routes/health.ts. curl -i localhost:3000/healthz returns 200 with body {\"status\":\"ok\"}. Added tests/health.test.ts; npm test: 14 passed."
})

The task is now claimed, and devops is permanently recorded as the prover. If devops tries to call verify on this task, the server returns E_SELF_VERIFY and the state does not change. Hand off to the second identity.

Step 3: a second agent verifies, fail-first

In the reviewer session, the verifier independently checks each criterion against the actual system: runs the tests, hits the endpoint, reads the code. It does not take the evidence at its word.

Suppose the endpoint works but the test was never committed. The verifier rejects, and notes are required on reject; name the specific gap:

verify({
  "task_id": "<task_id>",
  "verifier_agent_id": "reviewer",
  "verdict": "rejected",
  "notes": "Criteria 1 and 2 pass: GET /healthz returns 200 with {\"status\":\"ok\"}. Criterion 3 fails: tests/health.test.ts does not exist in the repo; npm test runs 13 tests, none cover healthz."
})

The task is now rejected, and that is terminal. There is no reopen. Remediation is a new task that supersedes the dead one, carrying the lineage:

create_task({
  "title": "Add /healthz endpoint to the api service (remediation)",
  "acceptance_criteria": [
    "GET /healthz on the running service returns HTTP 200",
    "Response body is JSON containing {\"status\": \"ok\"}",
    "npm test passes with the new healthz test included"
  ],
  "created_by": "reviewer",
  "supersedes": "<rejected task_id>"
})

The worker fixes the gap (commits the test), claims done on the new task with updated evidence, and the verifier checks again:

verify({
  "task_id": "<new task_id>",
  "verifier_agent_id": "reviewer",
  "verdict": "verified",
  "notes": "All three criteria checked directly: endpoint returns 200 with correct body; tests/health.test.ts present; npm test 15 passed."
})

That is the full loop: one rejection, one superseding retry, one verified close. get_task on either task shows the full append-only transition history; supersedes links the retry back to the rejection.

Tool reference

| Tool | Effect | |------|--------| | create_task(title, acceptance_criteria[], created_by, supersedes?) | New pending task; requires ≥1 non-blank criterion | | claim_done(task_id, agent_id, evidence) | pending → claimed; permanently records prover + evidence | | verify(task_id, verifier_agent_id, verdict, notes?) | claimed → verified\|rejected; verifier must differ from claimer; notes required on reject; both outcomes terminal | | get_task(task_id) | Full record + append-only transition history | | list_tasks(status?, claimed_by?, limit?, cursor?) | Paginated summaries |

Errors are returned as tool results with isError: true and a JSON payload {code, message, current_status?}; codes include E_SELF_VERIFY, E_WRONG_STATE, E_NOT_FOUND, E_EMPTY_CRITERIA, E_EMPTY_EVIDENCE, E_MISSING_NOTES, E_INVALID_TITLE, E_INVALID_AGENT_ID, E_IDENTITY_MISMATCH.

Identity model (v1)

agent_id is caller-declared (normalized: lowercase, trimmed, ^[a-z0-9][a-z0-9._-]{0,63}$). Optionally, launch with --agent-id <id> or VG_AGENT_ID to lock the connection: any tool call declaring a different agent_id fails with E_IDENTITY_MISMATCH. The threat model is honest-but-lazy agents, not adversarial identity forgery; an adversarial agent inside your fleet has already won.

What this is NOT

  • Not an orchestrator. It does not schedule, route, or run agents.
  • Not a project-management tool. There are no assignees, priorities, sprints, or due dates.
  • Not a task board. list_tasks exists so agents can find work, not so humans can manage it.

It does one thing: gate completion behind independent verification. It composes with whatever orchestration, framework, or plumbing you already run; anything that speaks MCP can call it.

Contributing

npm install
npm run typecheck   # tsc --noEmit
npm test            # vitest (unit + integration)
npm run build

The PR bar is the same rule the tool enforces: every claim in a PR description must itself be verifiable. "Fixed the race in claim_done" needs the test that fails without the fix and passes with it.

License

MIT. A permissive license fits an integrity primitive: the gate is only useful if anyone can run it anywhere, unmodified or not.