Proofrail
Proofrail is an MCP server that helps an agent verify code claims and get a prioritized repair packet for the least-proven claim, then recheck progress.
proofrail_verify: Load the manifest, resolve anchors, run declared commands, score all claims, and return the least-proven repair packet (anchors, missing proof, reproducer, done condition). Can restrict to specific claim IDs or skip execution with
run=false.proofrail_focus: Return a single repair packet for the least-proven claim (or a specific
claimId), with control over execution:autoreuses cached runs and runs only never-run commands,alwaysre-runs everything,neverdoes not execute.proofrail_recheck: Re-run only the commands a specific claim depends on, re-resolve its anchors, and report
satisfied=truewhen the claim reaches score 1.0; also names the next least-proven claim.All tools accept an optional
projectRoot(must be inside an allowed root) andjevto request advisory judgments from a local TypeSafe Jev command; Jev output never affects the deterministic score.The server enforces safety: only manifest-declared commands run (no shell), files and cwd stay inside roots, commands are bounded by timeout/output cap, and it is read-only.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Proofrail I just edited src/calc.js; what's the least-proven claim and repair packet?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Proofrail
Proofrail is a small stdio MCP server for Claude Code and Codex. After an agent edits code, it asks Proofrail which claim about the code is least proven. Proofrail answers with one compact repair packet: exact source anchors, the proof that is missing, a reproducer, and a done condition. There is no repo-wide "looks good" verdict, only the next specific thing to prove.
Proofrail packet 1/4: percent-rounds-half-up — failing, score 0.00, deficit 2.00
Claim: percent(part, whole) rounds .5 upward, so percent(1, 8) is 13.
Anchors:
- src/calc.js:15-17 percent
15| export function percent(part, whole) {
16| return Math.round((part / whole) * 100);
17| }
Missing proof:
- [command/failed] command "unit": output lacks "percent rounds half up"
→ Make command "unit" produce exit 0 and output including "percent rounds half up".
Reproduce: node --test test/calc.test.js (cwd .)
Done when: all 1 proof for "percent-rounds-half-up" pass and all 1 anchor resolve (score 1.0 on recheck).
Next: Make command "unit" produce exit 0 and output including "percent rounds half up".
Jev: unavailable (no jev command configured (set manifest.jev or PROOFRAIL_JEV_CMD))How it works
The project declares a proof manifest (
proofrail.json): the commands Proofrail may run, and a list of claims. Each claim has source anchors and proofs.proofrail_verifyresolves every anchor against the current source, runs the declared commands the claims depend on, and scores each claim deterministically.The lowest-scoring claim (weighted) becomes the repair packet.
The agent applies the packet's
nextstep, then callsproofrail_recheckwith the claim id. Recheck re-runs only what that claim needs and reportssatisfiedplus the next least-proven claim.
Related MCP server: Infinite Code Next
Install
Requires Node 20 or newer. Proofrail builds itself on install, so it runs straight from GitHub:
npx --yes github:armsteadj1/proofrail --helpOr add it to a project:
npm install --save-dev github:armsteadj1/proofrailClaude Code
From the project directory:
claude mcp add proofrail -- npx --yes github:armsteadj1/proofrailOr in .mcp.json at the project root (shared with the team):
{
"mcpServers": {
"proofrail": {
"command": "npx",
"args": ["--yes", "github:armsteadj1/proofrail"]
}
}
}Claude Code starts MCP servers with the project as the working directory, which becomes Proofrail's allowed root.
Codex
Add to ~/.codex/config.toml (or the project's .codex/config.toml):
[mcp_servers.proofrail]
command = "npx"
args = ["--yes", "github:armsteadj1/proofrail"]Or from the CLI:
codex mcp add proofrail -- npx --yes github:armsteadj1/proofrailRestricting roots
By default the only allowed root is the server's working directory. Pass --root one or more times to allow other directories; every projectRoot a tool call passes must be inside an allowed root.
{ "command": "npx", "args": ["--yes", "github:armsteadj1/proofrail", "--root", "/abs/path/to/repo"] }Tools
Tool | What it does |
| Load the manifest, resolve anchors, run the needed declared commands, rank all claims, return the least-proven packet. |
| Return one packet. Defaults to the least-proven claim; |
| Re-run only the commands one claim depends on, re-resolve its anchors, return |
All three accept projectRoot and jev (see below). Results carry a text rendering plus structuredContent with the same data as JSON.
Suggested agent loop:
verify → apply packet.next → recheck(claim) until satisfied → verify againThe manifest
proofrail.json at the project root (or .proofrail/manifest.json). A JSON Schema ships in schema/proofrail.schema.json. The full example is in examples/tiny-calc.
{
"$schema": "node_modules/proofrail/schema/proofrail.schema.json",
"version": 1,
"commands": {
"unit": { "cmd": "node", "args": ["--test", "test/calc.test.js"], "timeoutMs": 60000 }
},
"claims": [
{
"id": "divide-rejects-zero",
"statement": "divide(a, 0) throws a RangeError instead of returning Infinity.",
"weight": 2,
"anchors": [{ "file": "src/calc.js", "symbol": "divide" }],
"proofs": [
{ "kind": "command", "command": "unit", "expect": { "exitCode": 0, "outputIncludes": "divide by zero throws" } },
{ "kind": "test", "file": "test/calc.test.js", "name": "divide by zero throws", "command": "unit" },
{ "kind": "file-contains", "file": "src/calc.js", "pattern": "throw new RangeError" }
],
"reproducer": "node --test test/calc.test.js",
"done": "A test named \"divide by zero throws\" exists and passes."
}
]
}Commands
commands.<name> is the only place executables come from. Each has cmd, args, optional cwd (must stay inside the root), env, timeoutMs (default 120 s, max 10 min), and maxOutputBytes (default 64 KiB, max 1 MiB).
A command that intentionally selects exactly one test may declare focusedTest:
{
"cmd": "npx",
"args": ["vitest", "run", "src/example.test.ts", "-t", "handles invalid input"],
"focusedTest": { "file": "src/example.test.ts", "name": "handles invalid input" }
}focusedTest must exactly match a test proof that references the command. When it does, exit 0 proves that test even if the reporter omits its full name (for example, Vitest's 1 passed, 83 skipped summary). Without focusedTest, the existing rule remains: the command output itself must mention the exact test name. A generic green command therefore cannot certify an unreported test.
Anchors
An anchor is file plus one of:
symbol: a declared name. Proofrail recognises common declaration forms in JavaScript, TypeScript, Python, Go, Rust, Ruby, Java, and C#, and finds the block end by braces or indentation.pattern: a regular expression; the first matching line is the anchor.lines: an explicit[start, end]range.
Anchors resolve to file:start-end with a short numbered snippet. An anchor that no longer resolves after a refactor drags the claim's score down, which is how stale claims surface.
Proofs
kind | checks | strength |
| declared command exits with | 1.0 |
| test name appears in the file and the command exits 0; output must mention the name unless the command's exact | 0.9 |
| test name appears in the file (not executed) | 0.6 |
| file contains | 0.4 |
| nothing; documents an unverifiable claim honestly | 0 |
Scoring and ranking
Every claim gets a deterministic score in [0, 1]:
proofScore = Σ strength(passed proofs) / Σ strength(all proofs) (0 if no proofs)
anchorFactor = resolved anchors / declared anchors (0.5 if none declared)
score = proofScore × anchorFactor
deficit = weight × (1 − score)Claims are ordered by deficit descending, then status (failing, unproven, pending, partial, proven), then unresolved anchor count, then non-passing proof count, then claim id. The first claim is the packet. Same inputs always produce the same packet.
Safety model
Only manifest commands run. Tool inputs carry claim ids and a project root, never command strings. Commands are spawned with
shell: falseand a fixed argument vector, so nothing in the manifest or tool input is shell-interpreted.Root boundary. Files and command working directories are resolved against the project root and rejected if they escape it lexically or via symlink. The project root itself must be inside a root the server was started with.
Bounded execution. Every command has a timeout (SIGTERM then SIGKILL) and an output cap. Truncation is reported, not hidden.
Read-only. Proofrail writes nothing. The manifest is trusted the way
package.jsonscripts are: review it like code.Harness protocol variables (for example
NODE_TEST_CONTEXT) are scrubbed from child environments so spawned test runners behave normally.
Optional Jev adapter
Proofrail can attach advisory judgments from TypeSafe's Jev to each packet. Proofrail never calls the network itself. Configure a local command; Proofrail writes one TypeSafe System One request to its stdin and expects the API-shaped response on stdout:
{ "model": "jev-latest", "state": { "claim": {}, "anchors": [], "proofs": [] },
"questions": { "proof_covers_claim": { "type": "noul", "instructions": "...", "criteria": {} },
"anchors_match_claim": { "type": "noul", "instructions": "..." },
"best_repair": { "type": "choice", "instructions": "...", "criteria": {} } } }Response: { "answers": { "<id>": { "type": "noul", "noul": 0.8 } | { "type": "choice", "choice": "add_or_fix_test", "confidence": 0.9 } } }.
Configure it either in the manifest:
"jev": { "cmd": "node", "args": ["scripts/jev-bridge.mjs"], "timeoutMs": 15000, "model": "jev-latest" }or with environment variables, which take precedence: PROOFRAIL_JEV_CMD, PROOFRAIL_JEV_ARGS (JSON array), PROOFRAIL_JEV_TIMEOUT_MS, PROOFRAIL_JEV_MODEL.
Pass jev: true to a tool to consult it. The packet's jev field is one of:
{ "status": "unavailable", "reason": ... }when nothing is configured or it was not requested;{ "status": "error", "reason": ... }when the command fails, times out, prints invalid JSON, or omits an answer;{ "status": "ok", "answers": ... }with the answers exactly as returned.
Proofrail never fabricates Jev output, and Jev answers never change the deterministic score. They are advice next to it.
CLI
The same engine is available without MCP, useful in CI or for trying a manifest:
proofrail validate [dir] # parse the manifest
proofrail verify [dir] [--no-run] [--json] # exit 1 while any claim is unproven
proofrail focus [dir] [--claim id] [--run auto|always|never]
proofrail recheck <claimId> [dir] # exit 0 once satisfied
proofrail [--root dir]... # stdio MCP server (default)Development
npm install
npm test # builds, then runs the Node test suite (unit + CLI + stdio MCP smoke)
npm run schema # regenerate schema/proofrail.schema.json from the zod schema
node dist/cli.js verify examples/tiny-calcCI runs the suite on Node 20, 22, and 24 across Linux and macOS, checks the schema is current, and installs the checkout the way npx github: does.
License
MIT
Available Tools
3 toolsproofrail_focusGet one repair packet (least-proven or a specific claim)AIdempotent
Return a single repair packet. Without claimId it is the least-proven claim. run="auto" (default) reuses command results cached by an earlier verify/recheck and only runs commands that have never run; "never" never executes; "always" re-runs everything the manifest needs.
Inputs: jev, run, claimId, projectRoot.
proofrail_recheckRe-run only what one claim needs and report whether it is now provenAIdempotent
Re-run just the manifest commands referenced by the given claim, re-resolve its anchors, and return satisfied=true when the claim reaches score 1.0. Also names the next least-proven claim so the loop can continue.
Inputs: claimId (required), jev, projectRoot.
proofrail_verifyVerify claims and return the least-proven repair packetAIdempotent
Load proofrail.json, resolve every anchor, run the manifest-declared commands the claims depend on, score every claim deterministically, and return the ranking plus one compact repair packet for the least-proven claim (anchors, missing proof, reproducer, done condition). Set run=false to score from cached command results without executing anything.
Inputs: jev, run, claimIds, projectRoot.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 3 tools
The three tools share a common domain (proof verification) and the verify/focus tools both produce repair packets, but their purposes are distinct: verify scores all claims, focus returns a single repair packet, and recheck re-runs for a specific claim. The descriptions clarify the differences, though the overlap between verify and focus could cause occasional misselection.
All tools follow the same pattern: proofrail_<verb> (verify, focus, recheck). The naming is consistent snake_case with a clear prefix, making the tool set predictable and easy to navigate.
Three tools is a well-scoped number for this domain—each tool serves a distinct role in the verification workflow (overall scoring, targeted repair, and incremental re-checking). No unnecessary bloat, and all three earn their place.
The toolset covers the core lifecycle: verify for full evaluation, focus for repair guidance, and recheck for confirming fixes. Minor gaps exist (e.g., no explicit tool for updating the manifest or listing all claims), but agents can work around them using the existing tools and the repair packet details.
Maintenance
Related MCP Connectors
Browser-backed QA with evidence and fix-ready reports for coding agents.
Change-aware CI validation and affected-test guidance for coding agents.
Source-backed prompting and coding-agent guidance with provenance and freshness checks.
One-stop claim verification for AI agents: verdicts with confidence, evidence, signed receipts.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceA multi-agent system that autonomously analyzes code, proves bugs with formal certificates, generates repairs, and validates patches, all over the Model Context Protocol.40 npmMIT
- FlicenseNot gradedqualityBmaintenanceEnables AI coding agents to keep persistent, verifiable memory of a codebase, including the reasons behind code, prior rejected approaches, and invariants, anchored to the code and carried along as the code moves.-
- AlicenseBqualityBmaintenanceEnables coding agents to develop on one machine and verify results on another, using branch-bound runbooks, isolated checkpoints, and structured receipts to guide repair iterations.1051 npmMIT
- AlicenseAqualityCmaintenanceProvides AI coding agents with reliable root-cause diagnosis and minimal, high-confidence fixes for failing tests.31MIT