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., "@ProofrailI 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: Buggy
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.
| Name | Required | Description | Default |
|---|---|---|---|
| jev | No | Also ask the configured local Jev command for advisory judgments. Reports "unavailable" when none is configured. | |
| run | No | ||
| claimId | No | Claim id to focus on instead of the least-proven one. | |
| projectRoot | No | Project directory containing proofrail.json. Must be inside a root the server was started with. Defaults to the first allowed root. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description explains the run modes (auto/never/always) and the reuse of cached command results, which goes beyond what the annotations convey. The annotations already provide idempotent/decent safety hints, and the description adds meaningful execution behavior without contradicting them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: it states the primary operation first, then the default selection behavior, then the run modes. Every sentence contributes necessary information without padding.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given four optional parameters, no output schema, and the presence of sibling tools, the description covers the core semantics of the tool well, including the least-proven default and run behavior. It does not elaborate on the shape of a repair packet or error cases, but those are not essential for selecting and invoking the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 75%, with run lacking a description; the description remedies this by explaining all three run enum values and their effect. It also clarifies the default behavior when claimId is absent, adding value beyond the schema's field descriptions.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states it returns a single repair packet, with a concrete default (the least-proven claim) and an optional claimId override. It does not explicitly contrast itself with the sibling tools proofrail_verify and proofrail_recheck, but the resource and focus are clear enough to distinguish it from them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when the tool is used by referencing results 'cached by an earlier verify/recheck', which situates it in a workflow. However, it never explicitly says when to choose this tool over proofrail_verify or proofrail_recheck, or when not to use it.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| jev | No | Also ask the configured local Jev command for advisory judgments. Reports "unavailable" when none is configured. | |
| claimId | Yes | Claim id from a previous packet. | |
| projectRoot | No | Project directory containing proofrail.json. Must be inside a root the server was started with. Defaults to the first allowed root. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations (idempotentHint=true, readOnlyHint=false), the description adds genuinely useful behavior: it re-executes manifest commands, re-resolves anchors, gates success on a specific score threshold (1.0), and returns a follow-up recommendation (the next least-proven claim). It also discloses the advisory Jev behavior via the schema parameter description. It omits failure-mode behavior (e.g., unresolvable anchors), but the core execution semantics are transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two dense sentences with zero filler. The core scoping claim ('Re-run just the manifest commands referenced by the given claim') is front-loaded, and the second sentence adds only the loop-continuation value. The title also carries distinct meaning rather than echoing the name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with no output schema, the description compensates by naming the key return elements (satisfied=true at score 1.0, the next least-proven claim). The three parameters are fully documented in the schema, including the projectRoot constraint. Minor gaps remain — response shape beyond 'satisfied', and error behavior when a claim cannot reach score 1.0 — but the description is largely complete for its complexity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3 and the description need not restate parameters. The description does add light semantic linkage ('given claim' → claimId, 'configured local Jev command' → jev), but no parameter-level detail beyond what the schema already provides. That is exactly the baseline case.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('re-run'), a precise resource ('manifest commands referenced by the given claim'), a defined success criterion ('satisfied=true when the claim reaches score 1.0'), and a distinguishing scope ('only what one claim needs'). The title reinforces the 'recheck one claim' scope, which clearly separates it from siblings proofrail_verify and proofrail_focus.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage context is implied rather than explicit: the word 'just' contrasts with broader verification, and 'so the loop can continue' hints at an iterative workflow. However, no sibling is named, and there are no when-to-use vs. when-not-to-use conditions or explicit alternatives. The agent must infer the decision boundary from the scoping language.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| jev | No | Also ask the configured local Jev command for advisory judgments. Reports "unavailable" when none is configured. | |
| run | No | Execute the declared commands (default true). false uses cached results; dynamic proofs without a cached run report not-run. | |
| claimIds | No | Restrict verification to these claim ids. | |
| projectRoot | No | Project directory containing proofrail.json. Must be inside a root the server was started with. Defaults to the first allowed root. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already indicate non-read-only, non-destructive, and idempotent behavior. The description adds that it executes manifest-declared commands and scores deterministically, and that run=false skips execution. This adds behavioral context beyond annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single, dense sentence that front-loads the core action and appends the run=false caveat. It is efficient with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description covers the main behavior, the return format (ranking plus repair packet with components), and the run=false option. Given the tool's complexity and lack of output schema, it is reasonably complete, though it could detail the ranking structure more.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema covers all four parameters with descriptions, and the description repeats the run=false behavior without adding new information. With 100% schema coverage, the description adds no extra semantic value, so baseline 3 applies.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states it loads proofrail.json, resolves anchors, runs manifest-declared commands, scores claims deterministically, and returns a ranking plus one repair packet. This is a specific verb and resource, and it distinguishes from typical tools by describing the full verification pipeline.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides no guidance on when to use this tool versus proofrail_focus or proofrail_recheck. It only mentions the run=false option for using cached results, which is a parameter behavior, not a tool-selection guideline.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.0- First observed
proofrail_focus - First observed
proofrail_recheck - First observed
proofrail_verify
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.
Preflight QA for AI-agent deliverables with structured verdicts and repair guidance.
Codebase intelligence for agents: 152 structured artifacts across 21 programs, one call.
Adaptive plan/build/review cycles for AI coding assistants, persisted across sessions.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceGives AI coding agents a closed-loop verification cycle for visual, audio, and video output, with enforcement hooks that make verification mandatory.Apache 2.0
- 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.7 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.-
- AlicenseBqualityCmaintenanceEnables AI agents to verify technical claims against supplied evidence, identify unsupported assumptions and contradictions, and recommend the smallest next check before acting.5MIT