Skip to main content

MCP server that exposes Lean proofs in the Paperproof boxes format, without installing Paperproof

Project description

paperproof-mcp

MCP server that exposes Lean proofs in the Paperproof "boxes" format (NaturalProofTree) - without requiring Paperproof to be installed in the user's project.

Given a Lean file, the paperproof_proof_tree tool returns each theorem's proof as a hierarchical tree of boxes, where each box is one proof goal with the tactics that transform or close it - exactly the structure Paperproof draws.

How it works

The server runs Paperproof's real parser inside your Lean project, without you installing anything:

  1. It injects import Lean + Paperproof's bundled parser + a #ppjson in command into your file in-memory (via the LSP, same trick #info_trees in uses).
  2. Runs the parser on the target theorem and reads the resulting JSON off the diagnostics.

The bundled parser depends only on core Lean (no Mathlib), so it elaborates in any project. Because it's Paperproof's actual BetterParser, have/calc/case-splits are handled faithfully - including hypothesis-origin (from) links.

No Paperproof dependency, no lakefile edit, no rebuild. Only a working lake serve is needed (same requirement as lean-lsp-mcp).

Setup

Prerequisites

  • uv
  • A Lean project that builds (lake build)

Claude Code - add once, works in every project

claude mcp add -s user paperproof uvx paperproof-mcp

(-s user installs at user scope so you don't repeat it per-project; the server infers each project root from the file path you pass.)

VS Code / Cursor - add to mcp.json

{
  "servers": {
    "paperproof": {
      "type": "stdio",
      "command": "uvx",
      "args": ["paperproof-mcp"]
    }
  }
}

Tool

paperproof_proof_tree

Parameters:

  • file_path (required) - absolute path to a .lean file
  • theorem_name (optional) - a specific theorem; omit to parse all theorems in the file
  • format (optional, default "paperproof") - "paperproof" returns the hierarchical box tree (NaturalProofTree); "infoview" returns a flat before/after list per tactic (see below)
  • include_theorems (optional, default false) - also return the signatures of every theorem/def/axiom the proof references, in each tree's used_theorems

Returns: { lean_version, trees: [{ theorem_name, proof_tree, used_theorems }] }, where each proof_tree is in the requested format.

format: "infoview"

A flat, tactic-by-tactic before/after view, like Lean's infoview. Every goal shows its full hypothesis context (not a diff), and the theorem's initial hypotheses are listed once and never repeated:

{
  "initialHypotheses": [{ "name": "a", "type": "Nat" }, { "name": "h", "type": "a = b" }],
  "tactics": [
    {
      "tactic": "cases n with",
      "beforeGoals": [{ "hypotheses": [], "goal": "n = n" }],
      "afterGoals": [
        { "hypotheses": [], "goal": "0 = 0" },
        { "hypotheses": [{ "name": "k", "type": "Nat" }], "goal": "k + 1 = k + 1" }
      ]
    }
  ]
}

A tactic that closes its goal has an empty afterGoals; a case split or have obligation produces multiple afterGoals.

used_theorems

When include_theorems=true, each tree carries the lemmas it uses:

"used_theorems": [
  { "kind": "def",     "name": "AddCircle.liftIco", "signature": "{𝕜 : Type u_1} → ..." },
  { "kind": "theorem", "name": "Function.comp_apply", "signature": "∀ {β δ α} {f : β → δ} {g : α → β} {x}, (f ∘ g) x = f (g x)" }
]

Referenced constants are collected by walking the proof's info tree and keeping every identifier the user wrote that resolves to a global constant - this targets exactly the lemmas named in the source and drops notation plumbing (HAdd.hAdd, OfNat.ofNat, ...) that Expr.getUsedConstants would include. kind is derived from the type (Prop-valued => theorem), so it stays correct even under Lean's module system where imported proofs are erased and constants otherwise appear as axioms.

Example for have h2 : b = a := by rw [h]; exact h2:

{
  "tactic": "have h2 : b = a := by",
  "newHyps": [{ "name": "h2", "type": "b = a" }],
  "haveBoxes": [
    {
      "goal": "b = a",
      "newHyps": [],
      "tactics": [
        { "tactic": "rw [h]", "newGoal": "b = b" },
        { "tactic": "rw [rfl]", "closed": true }
      ]
    }
  ]
}

NaturalProofTree shape

NaturalProofTree  (root box)
  goal      : string          - goal at the top of this box
  newHyps   : NaturalHyp[]    - hypotheses introduced on entry
  tactics   : NaturalStep[]   - proof steps
  format    : "unicode"

NaturalHyp
  name  : string
  type  : string
  from? : string              - name of the hyp this one derives from (drives the arrow)

NaturalStep
  tactic       : string
  dependsOn?   : string[]      - hyp names this step consumes
  newHyps?     : NaturalHyp[]  - hyps this step introduces
  newGoal?     : string        - new goal if the goal transformed
  closed?      : true          - this step closes the current goal
  newSubgoals? : NaturalBox[]  - sub-boxes, one per case after a split
  haveBoxes?   : NaturalBox[]  - sub-proof(s) of a `have`/anonymous `by`

Lean version support

The parser must compile against your project's Lean version. Parsers are hand-maintained Lean files in src/paperproof_mcp/lean_parsers/vX.Y.Z.lean, each containing Paperproof's parser plus the #ppjson runner. The server picks the one matching your lean-toolchain; with no exact match it tries the nearest lower version, then the nearest higher.

To add a version, copy the closest existing parser to vX.Y.Z.lean and fix the Lean-API errors for that toolchain (they are usually small stdlib renames; e.g. for 4.25 vs 4.27: Substring.Raw -> Substring, .trimAscii -> .trim, .dropEndWhile -> .dropRightWhile).

Bundled versions: v4.25.0, v4.27.0. Module-system toolchains (Lean's module / public import / public section) are handled by the runner automatically: it marks the parser meta and meta imports the file's own imports.

Development

src/paperproof_mcp/
  server.py        - MCP tool; orchestrates the pipeline
  lean_runner.py   - in-memory injection + revert, reads JSON off diagnostics
  converter.py     - port of Paperproof's converter.ts (LeanProofTree to ConvertedProofTree)
  natural.py       - port of copyAsNaturalProofTree.ts (ConvertedProofTree to NaturalProofTree)
  lean_parsers/    - hand-maintained Paperproof parser + runner, one per Lean version

License

MIT (see LICENSE). This package bundles Paperproof's Lean parser, which is MIT licensed, Copyright (c) 2023 Anton Kovsharov.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

paperproof_mcp-0.1.1.tar.gz (22.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

paperproof_mcp-0.1.1-py3-none-any.whl (32.6 kB view details)

Uploaded Python 3

File details

Details for the file paperproof_mcp-0.1.1.tar.gz.

File metadata

  • Download URL: paperproof_mcp-0.1.1.tar.gz
  • Upload date:
  • Size: 22.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.4

File hashes

Hashes for paperproof_mcp-0.1.1.tar.gz
Algorithm Hash digest
SHA256 155b263e9e8e3e3af064ab5e5a4c7422d95f1da7702abd273958eaa3785f23d6
MD5 331850638183640cfd230c5a59277fdc
BLAKE2b-256 642a7ca8dd600fbeb7836e9e8d4f69350306bcb66ba07824ca1e721ae0d2290d

See more details on using hashes here.

File details

Details for the file paperproof_mcp-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for paperproof_mcp-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 09f7d016efcb8de9f342a41fe65453bf2a4608e22518cbc981a67dbd76e768ab
MD5 f209d9e98738a6ceaac148018e6e872a
BLAKE2b-256 485e4fa18c7be8a59c27cbaba3a8c5d304c636ac8a50f2690349e4e5ea44dd6d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page