使用说明
lean-mathlib-mcp
A machine-first MCP server for structured, low-ambiguity search over the Lean Mathlib 4 knowledge base (definitions, theorems, lemmas, notations, modules, instances, typeclasses, axioms).
It is a ground-up rewrite of the reference implementation
CriticalLine/lean-mathlib-docs-mcp,
re-targeted from "string search for humans" to "structured semantic retrieval
for LLM agents".
Design principles
- Machine-first. Every tool returns a single JSON string with a stable schema — directly parseable, no prose dumps.
- Precision over fuzzy semantics. Exact qualified name > short name + namespace > signature/conclusion structure > domain + intent.
- Complete metadata. A unified entity model covers definitions / theorems / lemmas / notations / modules / instances / typeclasses / axioms, with source, dependencies, status, and a stable id.
- Controllable granularity. Per-call
terse | summary | complete | expanded, plusmax_results, cursor pagination, and a softtoken_budget. - Read-only, structured errors. No write/exec path. Failures return an error code + corrective suggestions + negative-result feedback.
Architecture
docgen master index declaration-data.bmp ──► MasterIndexSource.load()
│ (neutral SourceBundle)
▼
MathlibIndex (compact secondary index + lazy entity materialization)
│
┌───────────────┬───────────────┬───────┴────┬──────────────────┐
▼ ▼ ▼ ▼ ▼
search/identity search/semantic search/context search/scoring sources/
(exact identity) (semantic feats) (graph context) (relevance rank) docgen_detail
│ │ │ (optional enrichment)
└───────────────┴───────────────┴──────────────────────────────┘
▼
output.assemble() → SearchResult (JSON)
▼
MCPServer (stdio) → 15 @server.tool
sources/—SourceBundleis a neutral data contract.MasterIndexSourceparses the official master index;DocgenDetailProvideroptionally supplies type signatures / docs / true dependency graphs (graceful degradation if absent).index.py— the production master index has 425k+ declarations. To avoid hundreds of MB of startup cost, a compactDeclMetais kept per declaration for filtering, and the rich entity object is materialized lazily, only for returned results.search/—identity(exact identity) /semantic(semantic features) /context(graph context) +scoring(weighted ranking).output.py— granularity projection + cursor pagination + token-budget truncation + reuse params + negative feedback.server.py—MCPServer("lean-mathlib-mcp")registers 15 tools via@server.tool;_dispatchroutes all calls (sync request/response; the batch tool shares the same path).
Data sources & enrichment
The master index declaration-data.bmp (official docgen export) contains the
declarations / modules / instances / instancesFor sections, but not type
signatures, docs, or true dependency graphs. Those rich features come from an
optional docgen4 JSON export:
- Without
docgen_detail_path,search_by_signature/search_by_conclusion/search_hypothesis/search_proof_complexityand the "true dependencies" ofexpand_dependenciesreturn anenrichment_unavailableerror code with a fix suggestion instead of garbage. search_by_domain_use/search_declaration/search_by_notation/module_snapshotand the topology fallback (module import graph) are always available.
Installation
Requires Python 3.11+ (mcp>=2, pydantic>=2, pydantic-settings).
# from a clone of this repo
python -m pip install -e . # installs the `lean-mathlib-mcp` console script
python -m pip install -r requirements.txt # or pin manually
An offline sample index (data/sample_index.json, ~3.4k declarations / 115
modules) ships with the repo so the tests and a quick smoke run work without
downloading anything.
Running (stdio)
# via the installed console script
lean-mathlib-mcp
# or, without installing, via the module
LEAN_MATHLIB_DATA_SOURCE=sample python -m lean_mathlib_mcp
# or via the bundled launcher (adds src/ to sys.path automatically)
LEAN_MATHLIB_DATA_SOURCE=sample python run_server.py
LEAN_MATHLIB_DATA_SOURCE=auto(default): use the local sample/cache if present, otherwise download the production index.LEAN_MATHLIB_DATA_SOURCE=remote: force-download the full production index (network required).LEAN_MATHLIB_DATA_SOURCE=sample: force the committed offline fixture (deterministic).
Connecting from an MCP client (e.g. WorkBuddy)
Copy mcp.example.json to mcp.json, set command to your Python interpreter,
and trust the server in your connector manager. Example:
{
"servers": {
"lean_mathlib_mcp": {
"command": "python",
"args": ["run_server.py"],
"env": { "LEAN_MATHLIB_DATA_SOURCE": "auto" }
}
}
}
If the package is published to PyPI, uvx lean-mathlib-mcp works as the command.
Configuration
All knobs are environment-overridable (prefix LEAN_MATHLIB_), so the server
deploys read-only with zero code changes.
| Variable | Default | Description |
|---|---|---|
LEAN_MATHLIB_DATA_SOURCE |
auto |
auto / sample / remote |
LEAN_MATHLIB_DATA_URL |
docgen official URL | Production master index URL |
LEAN_MATHLIB_CACHE_DIR |
data/cache |
Index cache directory |
LEAN_MATHLIB_RAW_DIR |
data/raw |
Where declaration-data.bmp is stored |
LEAN_MATHLIB_SAMPLE |
data/sample_index.json |
Offline sample path |
LEAN_MATHLIB_BUILT_INDEX_PATH |
data/cache/index.json |
Built index cache |
LEAN_MATHLIB_DEFAULT_GRANULARITY |
summary |
Default output granularity |
LEAN_MATHLIB_DEFAULT_MAX_RESULTS |
20 |
Max hits per call |
LEAN_MATHLIB_DEFAULT_TOKEN_BUDGET |
6000 |
Soft token cap (results truncated to fit) |
LEAN_MATHLIB_MAX_DEPENDENCY_DEPTH |
3 |
Dependency expansion cap |
LEAN_MATHLIB_MATHLIB_ONLY |
false |
true excludes Init.* core-library basics |
LEAN_MATHLIB_DOCGEN_DETAIL_PATH |
(empty) | Optional docgen4 JSON export for rich features |
Tools (15, all read-only)
Tools marked ⚡ enrichment return
enrichment_unavailablewhenLEAN_MATHLIB_DOCGEN_DETAIL_PATHis unset. Shared output controls (most tools):granularity ∈ {terse,summary,complete,expanded},max_results,min_score,token_budget,scope_modules,exclude_modules,mathlib_only,exclude_deprecated,only_stable,cursor.
3.1 Exact-identity search
| Tool | Key params | Description |
|---|---|---|
search_declaration |
query, entity_types?, domain?, use_tags? |
Exact qualified-name resolution, else ranked by name precision + domain closeness + usage. Empty result carries negative_feedback. |
get_entity |
full_name, granularity=complete |
One entity by exact name or stable id decl:*. On name collision returns ambiguous_match with disambiguation candidates. |
search_by_notation |
symbol, latex? |
Resolve a notation by Unicode or LaTeX (e.g. ≤ / \le / \sum): precedence, associativity, bound entity, conflicting forms. |
3.2 Semantic-feature search
| Tool | Key params | Description |
|---|---|---|
search_by_signature ⚡ |
arity?, return_type?, param_types? (_/* wildcards) |
Type-signature structural match, ranked exact-unify > unifiable > structural. |
search_by_conclusion ⚡ |
pattern, symmetric?, monotonic? |
Match by conclusion structure (variable names ignored, operator tokens matched), e.g. x + y = y + x. |
search_by_domain_use |
domain?, subdomain?, use_tags?, entity_types? |
Domain + subdomain + intent tags + entity type; core/high-frequency first. Always available. |
search_hypothesis ⚡ |
required_typeclasses?, required_axioms?, constructive_only?, exclude_classical?, no_axiom? |
Filter theorems by premise (hypothesis) outline. |
search_proof_complexity ⚡ |
max_dep_depth?, technique?, has_short_version?, exclude_axiom? |
Filter by proof structure / dependency depth; recommends proof-entry lemmas. |
3.3 Association & context search
| Tool | Key params | Description |
|---|---|---|
expand_dependencies |
full_name, depth=2, exclude_stdlib?, only_mathlib=true, as_tree=true |
Recursive dependency expansion (tree/flat); enriched deps preferred, else module import-graph fallback. |
find_usages |
full_name, direct_only?, transitive=true |
Impact analysis: direct/indirect dependents + modules importing its module. |
recommend_related |
full_name, limit=15 |
"What to read next": same-module core pairings, equivalents/duals, generalizations/specializations, other instances of its typeclass. |
module_snapshot |
module |
Layered module overview: domain, priority tier, imports, imported-by count, intro/core/advanced exports, key instances. Suggests similar modules when not found. |
3.4 Service & batch
| Tool | Key params | Description |
|---|---|---|
search_modules |
query, scope_modules?, max_results=50 |
Find modules/namespaces by substring. |
index_info |
— | Index health: declaration/module counts, enrichment status, data source. |
batch_query |
queries: [{tool, args}, ...] |
Run multiple queries in one round-trip; per-item errors isolated. |
Output schema
SearchResult
{
"query": { /* echoed query params */ },
"total_matches": 326,
"returned": 5,
"next_cursor": "5", // pagination cursor; null when exhausted
"truncated_low_relevance": false,
"hits": [ /* SearchHit[] */ ],
"reuse_params": { "full_name": "...", "id": "decl:..." },
"negative_feedback": { // present only when hits is empty
"reason": "no matching entity for the given query",
"relax_suggestions": [ "..." ]
}
}
SearchHit
{
"entity": { /* MathlibEntity projection (per granularity) */ },
"score": 0.95, // 0..1 relevance
"tier": "exact", // exact | structural | semantic | domain | usage
"match_dimensions": [ { "dimension": "exact_name", "score": 1.0, "tier": "exact" } ],
"truncated": false,
"reuse_params": { "full_name": "...", "id": "decl:..." }
}
MathlibEntity (field names are stable across Mathlib versions)
id · entity_type · raw_kind · full_name · short_name · namespace ·
module · type_signature · statement · doc · domain · subdomain ·
use_tags · doc_link · relations (dependencies/dependents/equivalents/
generalizes/specializes/related) · status (deprecated/maintenance/
cache_key) · is_core · usage_frequency · enrichment_source.
granularity projection:
- terse:
id/full_name/entity_type/module/domain - summary: +
short_name/doc_link/use_tags/is_core/usage_frequency - complete: +
type_signature/statement/subdomain/relations(light) - expanded: +
doc/ fullrelations/status
Structured error
{
"code": "enrichment_unavailable", // see table below
"message": "this search needs the docgen detail export",
"fixable": true,
"suggested_params": { "docgen_detail_path": "<path to docgen JSON>" },
"disambiguation": [ /* on ambiguous_match */ ],
"negative_feedback": { /* on no_match */ }
}
| Error code | Trigger | Caller action |
|---|---|---|
unknown_tool |
Unknown tool name (fixable:false) |
Check tool name |
invalid_argument |
Missing required arg | Add full_name / query |
ambiguous_match |
Name collision | Disambiguate via namespace/module (see disambiguation) |
no_match |
No notation/query match | See negative_feedback.relax_suggestions |
not_found |
Entity/module missing | Use search_declaration |
index_unavailable |
Index build failed | Switch data_source=sample |
enrichment_unavailable |
Rich feature missing | Provide docgen_detail_path |
out_of_scope |
Out of range/filtered | Adjust scope_modules |
internal |
Server exception (fixable:false) |
Report |
Relevance ranking
score = 0.45·name + 0.20·structure + 0.15·domain + 0.10·usage + 0.10·stability;
tier order exact > structural > semantic > domain > usage. Collisions and
ambiguity are surfaced explicitly via ambiguous_match rather than guessed.
Extending
- New data source: implement the
DataSource/DetailProviderprotocols insources/__init__.py, produce aSourceBundle, and wire it intoserver.build_index(). - New detail provider: subclass
DocgenDetailProviderand feed an external export (signatures / proof terms) intobundle.enriched[name]. - New tool: add an
@server.toolfunction inserver.py(prefix the function name withtool_to avoid clashing with same-namedsearch.*helpers), add a branch in_dispatch, and reuseassemble/make_error.
Testing
# unit tests (20): index, search, server wiring
python -m pytest tests/ -q
# real MCP stdio end-to-end (spawns the server subprocess over the protocol)
python tests/e2e_stdio.py
Build / rebuild fixtures and cache:
python -m lean_mathlib_mcp.builder sample # slice multi-domain sample from the real master index
python -m lean_mathlib_mcp.builder build # build the index cache
python -m lean_mathlib_mcp.builder info # index statistics
Known limitations
- Rich features (signatures / conclusions / true dependencies / proof structure)
depend on the optional docgen4 export; without it the relevant tools return
enrichment_unavailableand degrade gracefully. - In the master index, declaration names have no
Mathlib.prefix (e.g.Add,Nat.add);mathlib_onlyis decided by whether the owning module is underMathlib.*(defaultfalse, i.e. core library is not filtered out). - The production index has 425k+ declarations; the first build downloads and parses
declaration-data.bmp(~67 MB), then reusesdata/cacheafterward.
Credits
Refactored from
CriticalLine/lean-mathlib-docs-mcp.
Data is sourced from the Lean community's
Mathlib 4 docgen.
License
MIT — see LICENSE.
