Lean MCP
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., "@Lean MCPcheck this Lean proof: theorem add_two : 1 + 1 = 2 := by rfl"
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.
Lean MCP
An MCP server that compiles Lean 4 code with the local Lean toolchain and
returns the compiler's feedback in a structured response. It exposes a single
tool, lean_check. Mathlib and the basic Lean library are available to the
compiled snippets.
What it is
lean-mcp is a small, self-contained Model Context Protocol (MCP) server for
proof work. A client (for example OpenCode) sends Lean source code to the
lean_check tool; the server writes the snippet into a Lake project, elaborates
it with the real Lean compiler, and returns a structured verdict. Success is
never taken from the model: a declaration counts as proved only if the Lean
elaborator accepts it. The server speaks MCP over stdio (src/index.ts).
Related MCP server: lean-mcp
Features
One tool with a fixed contract.
lean_checkaccepts a singlecodestring and returns exactlystatus,summaryanddiagnostics. The output shape is declared with Zod sostructuredContentstays spec-compliant (src/index.ts).Real compilation. Snippets are elaborated by the local Lean toolchain through
lake env lean --json; the verdict is the compiler's, not the model's (src/lean.ts).Parsed, structured diagnostics. Lean's JSON messages are parsed and rendered as
line:col: severity: textstrings (src/lean.ts).Errors, warnings and information all reach the model. A rejected proof returns its errors; a successful file keeps its warnings and
informationoutput, so the answers to#check/#print/#evalare not dropped (src/lean.ts).sorryis flagged. A file that compiles withsorryreturnsstatus: "success", but thesummarystates that the proof is unfinished, so a hollow proof is not mistaken for a real one (src/lean.ts).Actionable failure hints. On failure the
summaryappends a one-line hint keyed to the failure class (unsolved goals,unknown identifier, syntax errors, and others) (src/lean.ts).Serialized execution. Concurrent tool calls run through a queue and each gets its own scratch file, so results cannot cross (
src/lean.ts).Timeouts terminate the whole process tree. A stuck tactic is stopped with SIGTERM, then SIGKILL, and the server never blocks (
src/lean.ts,src/config.ts).Clean shutdown, no orphans. SIGTERM, SIGINT, SIGHUP or a closed stdin aborts in-flight compilations by process group, so no
lakeorleansubprocess is left running (src/index.ts,src/lean.ts).Structured failures when misconfigured. A missing or read-only project directory returns a structured tool error instead of a raw OS error (
src/lean.ts), and startup environment problems are reported on stderr (src/index.ts).
Tech stack
Component | Version | Source |
Node.js | >= 20 |
|
TypeScript | ^5.7 |
|
| ^1.12.0 |
|
| ^3.23.8 |
|
Lean 4 |
|
|
Mathlib |
|
|
Lake | shipped with the Lean toolchain |
|
Architecture
MCP client (e.g. OpenCode)
| stdio: tools/call lean_check { code }
v
src/index.ts McpServer "lean-mcp", registerTool "lean_check"
|
v
src/lean.ts LeanChecker: serial queue, timeout, process-group kill
| writes <LEAN_MCP_PROJECT_DIR>/.lean-mcp-tmp/Check_<uuid>.lean
v
lake env lean --json <file> cwd = LEAN_MCP_PROJECT_DIR
| newline-delimited JSON messages on stdout
v
parseJsonMessages -> classify -> { status, summary, diagnostics }The entry point is dist/index.js (package.json, bin and start). The
scratch file is written inside the Lake project so the snippet picks up Mathlib
through LEAN_PATH (src/lean.ts).
Getting started
Prerequisites
Node.js >= 20 (
package.json).Lean 4
v4.34.1, installed with elan (lean-project/lean-toolchain).
Install and build
npm install
npm run buildLean toolchain and Mathlib
# Lean 4 toolchain (matches lean-project/lean-toolchain)
elan toolchain install leanprover/lean4:v4.34.1
# Fetch Mathlib and its prebuilt .olean files
cd lean-project
lake exe cache get
lake buildOnce lake build has run, lean_check can compile code against Mathlib.
Compilation time depends on the Mathlib cache being present
(src/lean.ts).
Connect it to OpenCode
Add the server under mcp.servers in ~/.config/opencode/opencode.json:
{
"mcp": {
"servers": {
"lean": {
"type": "local",
"command": ["node", "/path/to/lean-mcp/dist/index.js"],
"environment": {
"LEAN_MCP_PROJECT_DIR": "/path/to/lean-mcp/lean-project"
}
}
}
}
}command is the executable followed by its arguments; naming the built entry
point dist/index.js is what starts the server. The server speaks MCP over
stdio (src/index.ts). Theorem search is deliberately not reimplemented here:
that is a separate server (lean-explore, a hosted MCP), and lean_check works
without it.
Usage
lean_check
Input
{ "code": "theorem add_two : 1 + 1 = 2 := by rfl" }Note:
exampleis a reserved keyword in Lean, sotheorem example : ...is invalid. Use a named theorem such astheorem add_two : ..., orexample : ...for an anonymous one.
Successful response
{
"status": "success",
"summary": "Lean kodu başarıyla derlendi.",
"diagnostics": []
}(summary reads: "Lean compiled the code successfully.")
Failed response
{
"status": "error",
"summary": "Lean kodu derlenemedi. İpucu: bilinmeyen bir isim kullanıldı; doğru lemma/sabit adını `#check` veya `lean-explore` ile doğrulayın.",
"diagnostics": ["3:11: error: Unknown constant `Nat.succcccc`"]
}(summary reads: "Lean could not compile the code. Hint: an unknown name was
used; verify the correct lemma/constant name with #check or lean-explore.")
The summary and hint strings above are the exact text src/lean.ts produces
in the current build; they are quoted verbatim. The compiler messages in
diagnostics come from Lean itself. status is only ever success or error.
Success is decided by whether the code actually compiles; the model's own
assessment is not used (src/lean.ts).
isError semantics. The tool is marked isError: true only when the tool
fails: the Lean toolchain is missing or the compilation times out. The compiler
rejecting the code is the normal, expected outcome and is returned as
isError: false with status: "error". The model then treats each failed
attempt as a compiler verdict to iterate on, not as a tool failure
(src/index.ts).
Information and warning messages. Messages other than severity: "error"
reach the model in diagnostics. This covers #check / #print / #eval
output (for example #check Nat.add_comm gives
Nat.add_comm (n m : Nat) : n + m = m + n) and the
declaration uses `sorry` warning. A file containing sorry compiles
(status: "success") but the summary says so, so an empty proof is not taken
as finished (src/lean.ts).
Failure hints. When compilation fails, the summary carries a one-line hint
keyed to the failure class (for example unsolved goals gets a tactic
suggestion; unknown identifier means verify the name with #check or
lean-explore; a syntax error means check the notation). Warning and information
messages that accompany the
error are kept in diagnostics, so the model gets maximum context from a single
attempt (src/lean.ts).
Configuration
Variable | Default | Description |
|
| Directory of the Lake project to elaborate against |
|
| Time limit for a single compilation attempt |
|
| Upper bound on captured compiler output |
|
| elan installation directory; its |
Design notes
JSON output parsing. Snippets are compiled with
lake env lean --json. Lean's JSON messages parse reliably; the receiver treats the proof as failed when any message hasseverity: "error". If only warnings and information are present the code is considered compiled, and the warnings are returned indiagnostics(src/lean.ts).Serial execution. Concurrent tool calls run through a queue, so every call gets its own unique temporary file and scratch files do not interleave (
src/lean.ts).Timeout.
simp,decideandnorm_numcalls can run forever. On timeout the process group is stopped with SIGTERM and then SIGKILL, and the model gets anerrorwith a simplification suggestion (src/lean.ts,src/config.ts).Temporary files. Snippets are written under
.lean-mcp-tmp/inside the project (so Mathlib is on Lean'sLEAN_PATH) and removed after every call. On startup the server clearsCheck_*.leanfiles left by a previous crash (src/lean.ts).
Operational notes
Clean shutdown. On SIGTERM / SIGINT / SIGHUP or a closed stdio connection the server terminates every in-flight compilation together with its process group (server, then
lake, thenlean) and exits immediately. This prevents a stuck tactic from continuing as an orphan process after the server dies. Operational messages go only to stderr; stdout is reserved for the MCP protocol (src/index.ts,src/lean.ts).Startup check. On start the server checks that
LEAN_MCP_PROJECT_DIRexists and thatlakeis reachable; it prints a warning to stderr when something is wrong (it does not stop; every call returns a structured error) (src/index.ts).Configuration errors are returned structurally. When the project directory is missing or not writable,
lean_checkreturns{"status":"error","summary":"Lean çalıştırılamadı.","diagnostics":[...]}withisError: true, and the cause and project directory appear indiagnostics(src/lean.ts).Security note. This server runs every Lean snippet supplied by the model or the calling client on the local machine;
#evalexecutes arbitrary computation. Expose it only to a trusted party (a trusted model provider), and not to remote or anonymous clients (src/lean.ts).Rebuild. After
npm run build, a running MCP process keeps the previous code in memory; restart the server (or reconnect the MCP connection in your client) for changes to take effect.
Status
Lean 4.34.1 and Mathlib v4.34.1 are installed and the cache has been fetched
(lean-project/lean-toolchain, lean-project/lakefile.lean).
Verified behaviors:
Tool contract: only
lean_check, only acodeinput, onlystatus/summary/diagnosticsin the output (scripts/smoke.mjs,scripts/smoke-call.mjs).A real compiler error is returned in
diagnosticswith its exact text (for example2:29: error: unsolved goals ⊢ False) (src/test/integration.test.ts).With
import Mathlib, tactics such asnorm_numandomegawork (src/test/integration.test.ts).theorem x : P := by sorrycompiles (Lean exit code 0) but thedeclaration uses `sorry`warning is passed to the model indiagnosticsand thesummarystates that the proof is unfinished; a hollow proof does not look finished (src/lean.ts,src/test/integration.test.ts).information-level output such as#check/#print/#evalis returned indiagnostics, so the model can query lemma statements (src/lean.ts,src/test/integration.test.ts).A wrong proof returns
status: "error"withisError: false(the tool ran, the code did not compile);isError: trueappears only when the toolchain is missing or a check times out (src/index.ts,src/lean.ts).A timeout never locks up the server; the process is terminated and the model is advised to simplify (
scripts/robustness.mjs).Concurrent calls are serialized and no scratch files leak (
scripts/stress.mjs,src/test/integration.test.ts).The same snippet with and without Mathlib tactics gives different results, proving the import path really works (
src/test/integration.test.ts).
Not done yet (out of scope for this pass): wiring into an OpenCode configuration and an end-to-end model trial through OpenChamber.
Development
npm run build # compile TypeScript
npm test # parser unit tests (do not need Lean)
npm run check # tsc + unit tests + smoke (fast CI)
npm run test:all # check + integration + smoke:call + stress + robustness
npm run test:integration # tests against the real compiler
npm run smoke # stdio handshake and tool schema check
npm run smoke:call # end-to-end verification of a tool call
npm run smoke:stress # concurrent calls and scratch-file leak check
npm run robustness # production behaviors: broken project dir, process-tree
# cleanup during SIGTERM, timeout regressionIntegration tests skip themselves automatically when lake is not on PATH.
They also check whether Mathlib has been built; if
.lake/packages/mathlib has not been fetched, that single test is skipped
(src/test/integration.test.ts).
License
GPL-3.0-only. If you modify and distribute this repository, your derivative work must also be licensed under GPL-3.0. See the LICENSE file for the full source text.
Lean, Mathlib and the MCP SDK are out of scope; they are distributed under their own licenses.
Available Tools
1 toollean_checkLean Kodunu DerleA
Verilen Lean 4 kodunu yerel Lean derleyicisiyle çalıştırır. Mathlib ve temel Lean kütüphanesi kullanılabilir. Derleme başarılıysa status=success, başarısızsa derleyicinin hata mesajları diagnostics içinde döner.
| Name | Required | Description | Default |
|---|---|---|---|
| code | Yes | Çalıştırılacak Lean 4 kaynak kodu. |
Output Schema
| Name | Required | Description |
|---|---|---|
| status | Yes | Derleme sonucunun durumu. |
| summary | Yes | Sonucun kısa açıklaması. |
| diagnostics | Yes | Derleyiciden gelen hata, uyarı ve bilgi mesajları. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full disclosure burden. It does reveal the outcome contract (status=success on success, compiler messages in diagnostics on failure) and the available libraries, but says nothing about side effects, execution limits/timeouts, sandboxing, or the fact that arbitrary user code is executed locally — a meaningful gap for an execution tool.
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?
Three short sentences, front-loaded with the core action and environment before the result semantics; nothing is wasted. The final sentence partially restates return values that the output schema already covers, a minor redundancy.
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 one-parameter execution tool with an output schema, the description covers the action, environment, and result contract adequately. However, with no annotations and no mention of execution constraints or side effects, it is only minimally complete for a tool that runs arbitrary code.
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?
There is a single parameter with 100% schema description coverage, so the schema already documents 'code' fully. The description adds no syntax, format, or constraint detail beyond restating that it accepts Lean 4 source, making the baseline 3 appropriate.
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 and resource: it runs the given Lean 4 code with the local Lean compiler. It also specifies the environment (Mathlib and the core Lean library are available), which removes ambiguity about what the tool operates on. No siblings exist, so no differentiation is needed.
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 is implied by the purpose — run Lean 4 code to check it compiles — but there is no explicit when-to-use, when-not-to-use, or alternative routing. With no sibling tools, the absence of alternatives is expected, yet no preconditions or limits on use are stated.
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.
1 tool update
v0.1.0- First observed
lean_check
TDQS
Scored across 1 tool
There is only one tool, so no overlap or misselection is possible. An agent cannot confuse lean_check with anything else.
A single snake_case noun-scoped name (lean_check) is internally consistent and clearly readable. Consistency cannot be violated with one tool.
A server named 'Lean MCP' implies a broader Lean workflow, yet it exposes only one execution tool. There is no tool for separate diagnostics retrieval, file handling, or environment information, making the surface very thin.
Execution plus diagnostics return covers the core 'run Lean code' loop, so the primary use case is served. However, related operations such as managing source files, listing library/version info, or iterative editing are absent, leaving notable gaps.
Maintenance
Related MCP Connectors
Lean 4 MCP server: compile, prove theorems, and formalize math with Mathlib.
Architecture compiler for AI code. 11 tools, 92 actions, 872 Lean4 proofs, 100/100 self-cert.
Compile code with thousands of compilers, inspect the assembly, and share godbolt.org links
Certified program synthesis. Pay per call in USDC via x402; refusals never billed.
Related MCP Servers
- AlicenseAqualityAmaintenanceEnables LLM agents to interact with the Lean theorem prover through the Language Server Protocol, providing tools for analyzing Lean projects, accessing diagnostics, goal states, documentation, and searching for theorems using both local and external search services.22204,827 PyPI530MIT
- AlicenseNot gradedqualityDmaintenanceEnables verification of Lean 4 mathematical proofs via MCP tools, allowing AI clients to compile and check theorems with Mathlib.1MIT
- AlicenseAqualityAmaintenanceEnables AI coding agents to perform Lean 4 formal verification, navigate project symbols offline, and inspect C FFI bindings.51Apache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables AI-driven formal proof search in Lean 4 by submitting theorems with sorry placeholders and running parallel LLM agents whose proposed edits are verified by the Lean compiler until a machine-checked proof is produced. Also provides Mathlib theorem search and job/attempt management tools.2MIT