Skip to main content

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.

Tests Formal Audit Python Protocol

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 and installs from this repository:

uv tool install git+https://github.com/manzzaano/leo-code.git   # 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 git+https://github.com/manzzaano/leo-code.git with Python ≥ 3.11. 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 .gitignore when 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-mcp or ~/.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] @ git+https://github.com/manzzaano/leo-code.git", 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 this repository 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 y se instala desde este repositorio:

uv tool install git+https://github.com/manzzaano/leo-code.git
leo-mcp doctor
claude mcp add leo-mcp -- leo-mcp

Requisitos: uv, o Python ≥ 3.11 con pip install git+https://github.com/manzzaano/leo-code.git. 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.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for leo-mcp 0.3.0
File Size Uploaded Provenance Actions
leo_mcp-0.3.0.tar.gz 149.5 kB With provenance Details for leo_mcp-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for leo-mcp 0.3.0
File Interpreter ABI Platform Actions
leo_mcp-0.3.0-py3-none-any.whl Python 3 none any Details for leo_mcp-0.3.0-py3-none-any.whl

Total release size: 269.9 kB

Release files / leo_mcp-0.3.0.tar.gz

Download URL leo_mcp-0.3.0.tar.gz
Size 149.5 kB
Tags Source
SHA-256 checksum
How to use checksums
2a18e57e2ddce89e508ece1d62c8883e732e44971ac5716cd17c2c3ead51f3b9
BLAKE2b-256 checksum
How to use checksums
c2d9e0e3aeca4ade39d98e686e6d91d0ecdf310df59cd75c68306a233bda7c45
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 26, 2026.

Transparency log

Release files / leo_mcp-0.3.0-py3-none-any.whl

Download URL leo_mcp-0.3.0-py3-none-any.whl
Size 120.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
934a93fddaac8976d9bcd0534288aa607458a8653bf2937ee30ff2cf55086828
BLAKE2b-256 checksum
How to use checksums
d0db98dd646f07b8b8c14916b3c061d89eaa8db2a6222f11f4df118a10b26d8c
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 26, 2026.

Transparency log

Security for leo-mcp 0.3.0

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.

Select from 2 files

Attestation for leo_mcp-0.3.0.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 26, 2026.

Transparency log

A 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.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page