LeanTool is a simple utility that connects LLMs with a "Code Interpreter" for Lean. This is implemented as tool calls from the LLM to the Lean executable, hence the name.
Current LLMs often have trouble with outputing code with correct Lean 4 syntax, due to the recent rapid changes in the Lean language and its libraries. By allowing LLMs to talk directly to Lean, they are given opportunities to fix their mistakes. Furthermore, Lean being an interactive theorem prover, there are many interactive features that are not well represented in training data. This utility includes some initial efforts on prompting the LLMs with instructions on using these interactive features to better produce proofs.
Our design goal is to be flexible: easy to plug into automated workflows, but can also be plugged into human-facing chat/copilot interfaces.
This is part of a broader effort to create safe and hallucination-free coding AIs.
sorrys. This facilitates breaking complex proof tasks down into simpler subtasks, e.g. draft-sketch-prove.run_tests. See blog posts for details.sorry) with a hammer tactic (LeanHammer by default).A demo of the OpenAI-compatible API server is up at http://www.codeproofarena.com:8800/v1. To use it, connect your app to the above URL as the API Base URL, "provider" as OpenAI or OpenAI-compatible, "model" as one of the key names in the models dict in leantool.py, and API key as your API key for the chosen model. See below for specific set up details for OpenWebUI, Continue.dev, Cline and Aider.
http://ProvablyCorrectVibeCoding.com is a web front end to LeanTool and SafeVerify.
lake). E.g. for Linux/macOS:curl https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -sSf | sh
source ~/.elan/env # or restart your shell
uv sync
lake exe cache get
lake update
lake build
LiteLLM. E.g. for OpenAI, just set the environmental variable OPENAI_API_KEY.
For Anthropic, ANTHROPIC_API_KEY. If you want to try many different models, sign up for an OpenRouter
API key and set OPENROUTER_API_KEY. For local models served by ollama, start by installing ollama.
See Relevant LiteLLM Docs for more detailed instructions.
The models dict in leantool.py has some preset models; it has the format "short name" : "LiteLLM model name". Modify it to have an entry for your model
if you have something different.leantool.py is the Python library. Simply import the file and call interactive_lean_check to invoke the feedback loop.
Currently used by FormalizeWithTest autoformalization project,
and WakingUp experiments on hallucination detection.interactive_lean_check call. Here is a tentative interface design. A plugin is a python object that has the following members:
sys_msg: a string that will be attached to the system messageasync def process(self, code, result): a method that will be executed after the main Lean executable finishes. Takes in the LLM submitted code, and result a dict that records the results of the processing so far. The method should return the new result dict.cli_chat.py command line chat interface. Simply run uv run python cli_chat.py.app.py Streamlit chat interface.lean-api-server-flask.py OpenAI API compatible proxy server. Can be plugged into any application that takes a OpenAI API model with custom base URL.
Can either use the API keys set in the environment variables, or take an API key token in the request,
which is then passed to the corresponding LLM.
Has been tested to work with OpenWebUI, a fully featured chat interface,
and coding assistants Continue, Cline, and Aider.http://localhost:8000/v1:uv run python lean-api-server-flask.py
--add-host host.docker.internal:host-gateway -e OPENAI_API_BASE_URL=http://host.docker.internal:8000/v1apiBase url.
Set the model name to be the key name of your chosen model in the models dict in leantool.py, e.g. "sonnet".
For the apiKey field you may provide your API key for the chosen model.export OPENAI_API_BASE=<endpoint for the API server>
export OPENAI_API_KEY=<key for your chosen model>
# Prefix the model name with openai/
aider --model openai/sonnet
leanmcp.py is a Model Context Protocol (MCP) server. This exports Lean (and plugins including load_sorry) as a MCP tool, without the LLM and the feedback loop. Works with apps that can utilize MCP servers, and are able to manage the feedback loop within the app.
Has been tested to work with Claude Code, Claude Desktop, Cursor, VS Code Agent Mode, Gemini CLI, Cline, and Goose.leantool.py relevant to your use case, and put it in the corresponding settings for your coding assistant, e.g. CLAUDE.md for Claude Code or the "Rules for AI" setting in Cursor.stdio mode: e.g. when configuring your app for MCP, fill in the command uv run python leanmcp.pysse mode: e.g. run uv run python leanmcp.py --sse --port 8008,
then fill in the URL http://<your-host-or-ip-address>:8008/sse in your app's configuration.sse mode, but wants Claude Desktop (which only supports stdio) to connect to it, you can install configure Claude Desktop's MCP with{
"mcpServers": {
"LeanTool": {
"command": "npx",
"args": [
"-y",
"supergateway",
"--sse",
"<your-mcp-server>/sse"
]
}
}
}
claude mcp add LeanTool uv run python leanmcp.py
claude
sse mode, run the server with uv run python leanmcp.py --sse --port 8008,
then in Cursor, go to Cursor Settings > Features > MCP, click on the + Add New MCP Server button, and fill in the URL http://<your-host-or-ip-address>:8008/sse.sorry in place of the proof. Pass the code to the provided tool to check for syntax, and show me its output". Cursor will show the MCP tool call; you may need to click the Run tool button to approve the call.Python
92.5%
Lean
7.5%
LeanTool is a simple utility that connects LLMs with a "Code Interpreter" for Lean. This is implemented as tool calls from the LLM to the Lean executable, hence the name.
Current LLMs often have trouble with outputing code with correct Lean 4 syntax, due to the recent rapid changes in the Lean language and its libraries. By allowing LLMs to talk directly to Lean, they are given opportunities to fix their mistakes. Furthermore, Lean being an interactive theorem prover, there are many interactive features that are not well represented in training data. This utility includes some initial efforts on prompting the LLMs with instructions on using these interactive features to better produce proofs.
Our design goal is to be flexible: easy to plug into automated workflows, but can also be plugged into human-facing chat/copilot interfaces.
This is part of a broader effort to create safe and hallucination-free coding AIs.
sorrys. This facilitates breaking complex proof tasks down into simpler subtasks, e.g. draft-sketch-prove.run_tests. See blog posts for details.sorry) with a hammer tactic (LeanHammer by default).A demo of the OpenAI-compatible API server is up at http://www.codeproofarena.com:8800/v1. To use it, connect your app to the above URL as the API Base URL, "provider" as OpenAI or OpenAI-compatible, "model" as one of the key names in the models dict in leantool.py, and API key as your API key for the chosen model. See below for specific set up details for OpenWebUI, Continue.dev, Cline and Aider.
http://ProvablyCorrectVibeCoding.com is a web front end to LeanTool and SafeVerify.
lake). E.g. for Linux/macOS:curl https://raw.githubusercontent.com/leanprover/elan/master/elan-init.sh -sSf | sh
source ~/.elan/env # or restart your shell
uv sync
lake exe cache get
lake update
lake build
LiteLLM. E.g. for OpenAI, just set the environmental variable OPENAI_API_KEY.
For Anthropic, ANTHROPIC_API_KEY. If you want to try many different models, sign up for an OpenRouter
API key and set OPENROUTER_API_KEY. For local models served by ollama, start by installing ollama.
See Relevant LiteLLM Docs for more detailed instructions.
The models dict in leantool.py has some preset models; it has the format "short name" : "LiteLLM model name". Modify it to have an entry for your model
if you have something different.leantool.py is the Python library. Simply import the file and call interactive_lean_check to invoke the feedback loop.
Currently used by FormalizeWithTest autoformalization project,
and WakingUp experiments on hallucination detection.interactive_lean_check call. Here is a tentative interface design. A plugin is a python object that has the following members:
sys_msg: a string that will be attached to the system messageasync def process(self, code, result): a method that will be executed after the main Lean executable finishes. Takes in the LLM submitted code, and result a dict that records the results of the processing so far. The method should return the new result dict.cli_chat.py command line chat interface. Simply run uv run python cli_chat.py.app.py Streamlit chat interface.lean-api-server-flask.py OpenAI API compatible proxy server. Can be plugged into any application that takes a OpenAI API model with custom base URL.
Can either use the API keys set in the environment variables, or take an API key token in the request,
which is then passed to the corresponding LLM.
Has been tested to work with OpenWebUI, a fully featured chat interface,
and coding assistants Continue, Cline, and Aider.http://localhost:8000/v1:uv run python lean-api-server-flask.py
--add-host host.docker.internal:host-gateway -e OPENAI_API_BASE_URL=http://host.docker.internal:8000/v1apiBase url.
Set the model name to be the key name of your chosen model in the models dict in leantool.py, e.g. "sonnet".
For the apiKey field you may provide your API key for the chosen model.export OPENAI_API_BASE=<endpoint for the API server>
export OPENAI_API_KEY=<key for your chosen model>
# Prefix the model name with openai/
aider --model openai/sonnet
leanmcp.py is a Model Context Protocol (MCP) server. This exports Lean (and plugins including load_sorry) as a MCP tool, without the LLM and the feedback loop. Works with apps that can utilize MCP servers, and are able to manage the feedback loop within the app.
Has been tested to work with Claude Code, Claude Desktop, Cursor, VS Code Agent Mode, Gemini CLI, Cline, and Goose.leantool.py relevant to your use case, and put it in the corresponding settings for your coding assistant, e.g. CLAUDE.md for Claude Code or the "Rules for AI" setting in Cursor.stdio mode: e.g. when configuring your app for MCP, fill in the command uv run python leanmcp.pysse mode: e.g. run uv run python leanmcp.py --sse --port 8008,
then fill in the URL http://<your-host-or-ip-address>:8008/sse in your app's configuration.sse mode, but wants Claude Desktop (which only supports stdio) to connect to it, you can install configure Claude Desktop's MCP with{
"mcpServers": {
"LeanTool": {
"command": "npx",
"args": [
"-y",
"supergateway",
"--sse",
"<your-mcp-server>/sse"
]
}
}
}
claude mcp add LeanTool uv run python leanmcp.py
claude
sse mode, run the server with uv run python leanmcp.py --sse --port 8008,
then in Cursor, go to Cursor Settings > Features > MCP, click on the + Add New MCP Server button, and fill in the URL http://<your-host-or-ip-address>:8008/sse.sorry in place of the proof. Pass the code to the provided tool to check for syntax, and show me its output". Cursor will show the MCP tool call; you may need to click the Run tool button to approve the call.Python
92.5%
Lean
7.5%