Getting started¶
Install¶
Python 3.13 (the project's .venv). A bare python on this machine may be a different version — always go through uv run.
Your first run¶
Create example.py:
def average(total: int, count: int) -> int:
return total // count
def port_of(config: dict) -> int:
return config["port"]
def first(items: list) -> int:
return items[0]
Run it:
VERIFIED — 3 of 3 functions (100%)
CRASHES — 3, each with an input that triggers it
example.py:2: average: ZeroDivisionError: integer division or modulo by zero
reproduce: average(0, 0)
example.py:6: port_of: KeyError: 'port'
reproduce: port_of({})
example.py:10: first: IndexError: list index out of range
reproduce: first([])
Every reproducer is a real crash — paste average(0, 0) into a REPL and it raises. Exit code is 1 (a crash was proven), so this fails a CI gate.
Reading the report¶
The report is a ledger — every function lands in exactly one bucket, and the headline is VERIFIED — n of N, the fraction of the whole codebase mcpython could decide. On a toy file that's 100%; on real code it starts low and you drive it up.
Four sections, in order:
VERIFIED — n of N (p%)— the headline. The goal is 100%.CRASHES— the findings, each with afile:lineand a reproducer.- The proofs — the product. Functions proven crash-free (for every input their types allow), proven against a contract, or proven by one of your
@proofharnesses. All are named, so you can check them against what you believed. UNVERIFIED — n of N— a burn-down of what's left, ranked, with an owner on every line: YOUR MOVE (a flag or an annotation you can add) versus MCPYTHON OWES YOU (a modeling feature it doesn't have yet). This is the part that makes the number a plan instead of a grade.
mcpython makes NO claim about the unverified functions — that line is deliberate. "No crashes found" is not "your code is safe" when most of the code wasn't modeled, and the report never lets those be confused.
The flags¶
| Flag | Effect |
|---|---|
--conditional |
Also decide functions whose only blocker is an external call, assuming those calls return normally. Verdicts are tagged CONDITIONAL. Strictly more coverage, weaker guarantee. |
--json |
Per-function detail as JSON (status, reason, reproducer, contract, targets). For tooling and AI agents. |
-d / -dd / -ddd |
Trace the analysis to stderr (the report stays clean on stdout). -d: each verdict with file:line. -dd: parameter models, branches taken, each obligation discharged, and the actual SMT query (SAT/UNSAT + serialized formula). -ddd: also the inconclusive functions and why. The trace never changes a verdict. |
Going further¶
The default run finds crashes and proves what it can with zero annotation. To prove real properties — "this validator returns its input unchanged for every valid port", "this scan rejects every control character" — or to make verification scale across a codebase, you write a few lines using mcpython.harness. See harness-api.md.