leo-mcp
Give your coding agent the real call graph of your repo, with proof, instead of letting it guess by grepping and reading whole files.
| 100% call-graph precision and recall vs. Python's own ast |
80–97% fewer tokens per lookup vs. reading the files |
<2 ms slowest graph query at 271k indexed symbols |
Quickstart
leo-mcp is in beta, published on PyPI and npm:
uv tool install leo-mcp # installs the leo-mcp command
leo-mcp doctor # checks your setup and prints the config snippet
claude mcp add leo-mcp -- leo-mcp # Claude Code
Other clients: run leo-mcp init --client opencode|cursor|codex inside your project.
Requirements: uv, which fetches Python for you. Without uv: pip install leo-mcp with Python ≥ 3.11. Or without installing anything: claude mcp add leo-mcp -- npx -y leo-mcp.
No API key: leo-mcp never calls an LLM. It parses your repo locally.
What your agent gets
Two tools, plus server instructions that tell the agent when to use them.
graph gives deterministic answers from the parsed call graph, each one cited file:line:
> graph op=who_calls symbol=get_session
[who_calls] get_session — 10 result(s), with proof:
· execute_batch (method) @ src/agents/executor_agent.py:32
· _persist_jobs_sync (function) @ main.py:42
· analyze_and_report (method) @ src/agents/advisor_agent.py:38
· jobs (endpoint) @ src/api/app.py:94
...
| op | answers |
|---|---|
where |
where a symbol is defined |
who_calls |
its direct callers |
impact |
everything that transitively depends on it: what breaks if you change it |
trace |
the call path from A to B, including frontend fetch('/api/x') → backend route |
guard |
impact plus which affected symbols have no test |
get_context takes a question in plain language and returns the AST subgraph that answers it: the relevant symbols with their bodies, compressed. Every response says what it saved:
[leo-mcp · 1,381 tokens of code context · the 6 source files it draws from are ~7,891 tokens (-82%) · 476 symbols indexed]
CLI
leo-mcp index # index the current repo and show what leo-mcp sees
leo-mcp doctor # check the install, print the config snippet
leo-mcp init # add leo-mcp to this project's .mcp.json + the pre-edit guard hook
leo-mcp init --no-hook # ...without the hook
leo-mcp indexed C:\...\NEXUS (build in 0.17s)
476 symbols in 35 files
python 450
typescript 25
most depended-on (run graph op=guard before touching these):
click 11 callers src/automation/human_behavior.py:37
get_session 10 callers src/database/db_session.py:26
Languages, and what it doesn't do
- Full AST: Python (
ast) and TypeScript/TSX/JavaScript (tree-sitter), including functions declared as variables or object properties (const Page = () => …,const load = useCallback(() => …),api = { jobs: () => get("/api/jobs") }) and Python class attributes (min_match_score: int = 60). - Approximate (regex-level): Go, Java, Rust, C/C++, C#, Ruby, PHP, Kotlin, Swift and others.
- Skipped: dependencies, build output (
node_modules,.next,dist,target…), minified bundles, files over 512 KB, and anything in.gitignorewhen the repo uses git. - Not in the graph: it is a static graph. Calls through WebSockets, subprocesses, queues, reflection or string-built dispatch are not edges, and using a type (a dataclass in an annotation) is not a call. Values that merely come out of a call (
const rows = items.filter(…)) are not symbols. - Your files stay put. The index lives in your user cache (
%LOCALAPPDATA%\leo-mcpor~/.cache/leo-mcp), never inside your repo. It is kept up to date incrementally: a filesystem watcher re-parses only what actually changed, so a call that finds nothing new costs nothing (on a 20,000-file tree, re-scanning it cost 1.5-2.0 s per call).
Configuration
| Env var | Default | Effect |
|---|---|---|
LEO_REPO |
client's working dir | repo to index when a tool call gives no repo_path |
LEO_SEMANTIC |
off | semantic recall in get_context; needs the semantic extra (uv tool install "leo-mcp[semantic]", sentence-transformers, ~2 GB with torch). graph doesn't need it |
LEO_CACHE_DIR |
user cache | where indexes and logs go |
LEO_MAX_FILE_KB |
512 |
skip source files larger than this |
LEO_WATCH |
1 |
filesystem watcher; 0 falls back to re-scanning the tree (network mounts that drop events) |
LEO_GUARD_HOOK |
1 |
the warning before an edit; 0 silences it without touching your settings |
LEO_MCP_FROM |
leo-mcp==<version> |
source uvx uses when the leo-mcp command isn't installed (local wheel, git URL) |
It warns you before your agent breaks something
leo-mcp init also installs a PreToolUse hook in Claude Code. Before the agent edits a
symbol, leo looks up who depends on it. If some of those dependents have no test, it says
so — to the agent, so that it checks them, and to you, in one line:
leo: ensure_structural -> 208 dependents, 11 with no test
The rest of the time it says nothing, by design: a warning that fires on every edit is a warning you turn off. Dependents that are covered by a test raise nothing, and editing a test file raises nothing.
It costs 0.28 s per edit on a 1,220-symbol index. It never builds an index (no index yet → silence), it goes quiet above a 4 MB index instead of adding seconds to every edit, and it never blocks an edit: it has no way to. It emits no permission decision and always exits 0, and both invariants have tests.
Its silence is not a guarantee. Same static graph as the rest of the product: a dependency through a WebSocket, a queue, a subprocess or a name built from strings is not in it, so the list can be incomplete. It is a net, not a proof — the decision stays yours.
The guarantee: structural correctness, proven
graph doesn't estimate structure. It walks the parsed graph, and CI checks that graph against parsers that are not leo's. benchmark/audit_formal.py re-derives every edge with Python's ast and the TypeScript compiler tsc, compares edge by edge, and a regression can't merge (.github/workflows/audit.yml).
Last green CI run (FORMAL: 6/6 → IRREFUTABLE):
| Check | Scale | Measured |
|---|---|---|
(a) Python graph vs ast |
leo-code + 3 external repos (~100,400 symbols, ~480,200 edges) | 100.000% precision · 100.000% recall |
(a-ts) TS/JS graph vs tsc |
TypeScript compiler repo (36,739 symbols) | 99.950% precision · 99.926% recall (31 false / 46 missed of 62.2k edges) |
(b) guardian coverage vs coverage.py |
146 executed functions | 0 false in either direction |
| (c) blast radius vs real mutation testing | mutated classify_task, get_budget |
every failing test ⊆ predicted |
| (d) latency at scale | 271,493 indexed symbols | worst query 1.98 ms · guardian 13 ms |
| (e) post-edit budget | the graph rebuilt after one edit | ≤20 µs/symbol amortized, and the index is never written inside a tool call |
The TS/JS figure is left unrounded.
Measured with real agents
opencode, 15 tasks × 3 runs, full token accounting including subagents (2026-07-14):
| Metric | Threshold | Result |
|---|---|---|
| Answer quality (LLM judge) | with MCP ≥ without | ✅ +0.18 (5.00 vs 4.82) |
| Duration | ≤ +10% | ✅ −14.2% |
| End-to-end tokens | ≤ +10% median per task | ✅ +1.9% median · −30.1% pooled |
The honest claim: savings are per lookup and show up on heavy tasks (pooled −30%). On a typical task the token cost is neutral. What an agent without it can't get is a structurally correct answer. An early "−40% end-to-end" goal was dropped after six configurations measured worse or neutral.
Field test on a project that isn't this one (2026-09-15, NEXUS: FastAPI + Next.js/TSX, not a git repo, with 49 MB of saved web pages inside):
| Result | |
|---|---|
Install from scratch (uvx, cold cache) |
6 s, 114 MB, no torch |
| Indexed | 476 symbols (Python 450 · TypeScript 25); 59 of 60 minified bundles skipped |
| Index time | cold 1.0 s · reload from cache 0.05 s (CLI) |
graph latency |
< 10 ms |
get_context vs reading the files it covers |
−82% and −90% on the two sample questions |
| Files written inside the project | 0 (directory listing diffed before/after) |
Then real agents answered 8 questions about NEXUS whose answers are known, each with and without leo-mcp (read-only, n=1 per question):
| Claude Code + Sonnet | opencode + DeepSeek V4 Flash | |
|---|---|---|
| Tokens, median per task | −21% | −4% |
| Tokens, pooled | −26% | −19% |
| Duration | +0.4% | −2.2% |
| Questions where the agent used leo-mcp | 8/8 | 6/8 |
| Correct answers (without → with) | 7/8 → 7/8 | 7/8 → 7/8 |
Savings came from structural questions (impact of changing a type −59%, "what happens when I press Start" −69%). Simple lookups cost the same or more. With n=1 per question, treat per-task numbers as noisy.
The field test found and fixed several bugs a user would have hit: TS/TSX was never indexed through MCP, two first-use hangs on Windows (a git call and a native import, both blocked by the stdio pipe), a Python d.get() counted as a caller of a TypeScript get(), who_calls cited the caller's definition line instead of the call line, and asking about an HTTP endpoint returned its signature without the body.
How it works
source ─► AST (Python ast, tree-sitter) ─► capsules: functions/classes with calls, file:line
─► per-repo index in the user cache, updated incrementally
─► graph: where / who_calls / impact / trace / guard (no model involved)
─► get_context: exact + BM25 + structural scorer (+ semantic, optional) → RRF → compressed
Frontend/backend boundary: a fetch('/api/users') is stitched to the @app.get('/api/users') that serves it (core/boundary.py), so trace follows data across services. Edges never cross languages by name alone: a Python d.get() is not a caller of a TypeScript get().
leo_code/
├── cli.py # leo-mcp serve | index | doctor | init
├── engine.py # per-repo persistent index + hybrid retrieval + compression
├── core/ # parsers, graphquery (the graph), guardian, boundary
├── rag/ # indexer, compressor, bm25, scorer, optional vector store
└── server/mcp_server.py
npm/ # npm launcher for a future npm release (runs the Python package via uv)
Development
git clone https://github.com/manzzaano/leo-code.git && cd leo-code
pip install -e ".[dev]"
pytest -q # 279 tests, including an end-to-end stdio server test
node --test npm/bin/leo-mcp.test.js # launcher
python benchmark/mcp_client_bench.py # real MCP client, token-reduction gate
En español
leo-mcp es un servidor MCP que le da a tu agente de código (Claude Code, opencode, Cursor, Codex) el grafo de llamadas real de tu repo. Con él responde dónde se define algo, quién lo llama, qué se rompe si lo cambias y el camino de A a B, citando siempre archivo:línea. Sin LLM y sin API key.
Está en beta, publicado en PyPI y npm:
uv tool install leo-mcp
leo-mcp doctor
claude mcp add leo-mcp -- leo-mcp
Requisitos: uv, o Python ≥ 3.11 con pip install leo-mcp. El índice vive en la caché de usuario, nunca dentro de tu proyecto. Los números y la metodología están arriba, con los comandos para reproducirlos. El caso de estudio completo está en leosoftware.dev/sistemas/leo-mcp.
Metadata
Release files for leo-mcp 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| leo_mcp-0.3.3.tar.gz | 152.2 kB | With provenance | Details for leo_mcp-0.3.3.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| leo_mcp-0.3.3-py3-none-any.whl | Python 3 | none | any | Details for leo_mcp-0.3.3-py3-none-any.whl |
Total release size: 273.6 kB
Release files / leo_mcp-0.3.3.tar.gz
| Download URL | leo_mcp-0.3.3.tar.gz |
|---|---|
| Size | 152.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4839d395654f85b98ade71a2f4578940858409061dabf6917c29e24b8d803720
|
|
BLAKE2b-256 checksum How to use checksums |
622ea85ef2cc3cacda04900db7199eefe919cc5f81840e9173b5fb66fbe855f9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logRelease files / leo_mcp-0.3.3-py3-none-any.whl
| Download URL | leo_mcp-0.3.3-py3-none-any.whl |
|---|---|
| Size | 121.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a860540d8f8f5d1d37076ca1e0faffd958b9129bd89ff27b90580606977bf63e
|
|
BLAKE2b-256 checksum How to use checksums |
9eb19781769e676e2539bef1d7a3ffcc5c90e8e7ddf8eb1d356628116de86e41
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logSecurity for leo-mcp 0.3.3
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for leo_mcp-0.3.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 27, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.