verify-gate-mcp
Enforces that tasks are created with criteria, claimed by one agent with evidence, and verified by a different agent, preventing self-verification and ensuring independent validation.
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.
Why this exists
Three catches from running this gate in production, before open-sourcing it:
- A devops agent reported a "fix" for a P0 incident. The verify gate found no fix was actually running.
- 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.
- 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
- Criteria at birth: a task cannot exist without ≥1 non-empty acceptance criterion; there is no add-criteria-later tool.
- Prover recorded:
claim_donepermanently records the claiming agent and evidence. - Prover ≠ verifier:
verifyhard-rejects when the verifier is the claimer (E_SELF_VERIFY), for both verdicts. - Forward-only:
pending → claimed → (verified | rejected). Both end states are terminal; a rejected task's remediation is a new task withsupersedesset for lineage.
Install
Requires any MCP-capable agent client; Claude Code is the tested reference client.
From source (npm package coming soon):
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
Claude Code / MCP client config:
{
"mcpServers": {
"verify-gate": {
"command": "node",
"args": ["/path/to/verify-gate-mcp/dist/index.js", "--db", "/shared/tasks.db"],
"env": { "VG_AGENT_ID": "devops" }
}
}
}
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.
Tools
| Tool | Effect |
|---|---|
create_task(title, acceptance_criteria[], created_by, supersedes?) |
New pending task |
claim_done(task_id, agent_id, evidence) |
pending → claimed; records prover + evidence |
verify(task_id, verifier_agent_id, verdict, notes?) |
claimed → verified|rejected; verifier must differ from claimer; notes required on reject |
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_AGENT_ID, E_IDENTITY_MISMATCH.
State machine
create_task → pending → claim_done(A) → claimed → verify(B≠A) → verified | rejected
│
└ verify(A) → E_SELF_VERIFY (state unchanged)
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_tasksexists 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
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.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。