{"slug": "show-hn-lantern-lattice-the-call-graph-from-your-compiler-for-coding-agents", "title": "Show HN: Lantern Lattice – the call graph from your compiler, for coding agents", "summary": "Developer July Metodiev released Lantern Lattice, a command-line tool and MCP server that exposes compiler- and type-checker-resolved call graphs to coding agents, with support for Rust, TypeScript, JavaScript, Python, Go, C and C++, and Java listed as next. The tool answers \"who calls a function, what it calls, who uses a type or a field\" using rust-analyzer, tsrs, pyrefly, scip-go with gopls, and clangd with clangd-indexer, and replaces Grep, Glob, Read, Edit and Write with symbol-aware operations including trace, impact, search, read, edit_symbols, insert_after, grep, files, read_file, edit_lines, replace_text, write_file, sync and status. Lantern Lattice installs via Homebrew, a platform archive, or cargo with Rust 1.95 or newer, and its daemon stops after 30 idle minutes.", "body_md": "**The call graph from your compiler or type checker, for humans and coding agents**\n\nWho calls a function, what it calls, who uses a type or a field, and edits by symbol. `x.get()` links\nto the `get` of `x`'s type, because the language's own compiler resolves it, where a text search finds every `get`.\nIt also has the tools an agent needs instead of Grep, Glob, Read, Edit and Write, so the graph stays up\nto date as the agent works.\n\nOn macOS and Linux:\n\n| Language | Status | Answers come from | \n|---|---|---|\n| Rust | Supported | `rust-analyzer` | \n| TypeScript | Supported | `tsrs` | \n| JavaScript | Supported | `tsrs` | \n| Python | Supported | `pyrefly` | \n| Go | Supported | `scip-go` and`gopls` | \n| C | Supported | `clangd-indexer` and`clangd` | \n| C++ | Supported | `clangd-indexer` and`clangd` | \n| Java | Next |  | \n\n*Lantern Lattice's own code: [Live](https://lanternlattice.com/)*\n\n*A chosen symbol: what calls it, what it calls, what it uses, and its source.*\n\nWith [Homebrew](https://brew.sh), on macOS and Linux:\n\n```\nbrew install julymetodiev/tap/lantern-lattice\n```\n\nOr download an archive for your platform from the\n[latest release](https://github.com/julymetodiev/lantern-lattice/releases/latest) and put `latt` on your `PATH`.\nThe binaries are not signed, so on macOS one downloaded in a browser may need\n`xattr -d com.apple.quarantine latt`.\n\nOr build it, with Rust 1.95 or newer:\n\n```\ncargo install --git https://github.com/julymetodiev/lantern-lattice lantern-lattice\n```\n\nFor exact answers, Lantern Lattice needs `rust-analyzer` for Rust, [`tsrs`](https://github.com/oxc-project/tsrs) for TypeScript and JavaScript, [`pyrefly`](https://github.com/facebook/pyrefly) for Python, `go` with [`scip-go`](https://github.com/scip-code/scip-go) and `gopls` for Go, and `clangd` with `clangd-indexer` for C and C++. In a Python project, install its packages in a `.venv` at the top: without them less is answered. A C or C++ project needs a `compile_commands.json`, at the top or in `build/`, and Lantern Lattice never runs what makes one, as that runs the project's code.\n\n```\nrustup component add rust-analyzer rust-src\nnpm install -g tsrs\npipx install pyrefly\ngo install github.com/scip-code/scip-go/cmd/scip-go@latest\ngo install golang.org/x/tools/gopls@latest\n```\n\n`clangd` comes with `xcode-select --install` on a Mac and with the system's package on Linux. `clangd-indexer` is in `clangd_indexing_tools` of a [release of clangd](https://github.com/clangd/clangd/releases).\n\nIn a git repository, nothing else is needed. The first command starts a daemon, which stops after 30 idle minutes or when the project is gone.\n\n```\nlatt trace -s process_data              # who calls it, what it calls\nlatt trace -s process_data --callers    # only who calls it\nlatt impact                             # what the uncommitted changes touch, and what depends on them\nlatt impact --base main                 # the same for a whole branch\nlatt search --pattern \"^process\"        # symbols by regular expression\nlatt grep \"TODO\" --path \"*.rs\"          # text in any file, with the symbol it is in\nlatt read -s process_data               # the source of a symbol\nlatt files \"crates/**/*.rs\"             # files by glob, skipping what git ignores\nlatt cat src/lib.rs --first 10 --last 40  # a file, numbered\nlatt write src/new.rs --content \"fn new() {}\"  # a new file; --overwrite to replace one\nlatt edit --symbol process_data --mode symbol --content \"fn process_data() {}\"\nlatt edit --file src/lib.rs --mode text --old \"a = 1\" --content \"a = 2\"  # exact text, found once\nlatt sync                               # up to date, wait until nothing is provisional\n```\n\nThrough MCP, the same operations are tools the model can use: `trace`, `impact`, `search`, `read`,\n`edit_symbols`, `insert_after`, `grep`, `files`, `read_file`, `edit_lines`, `replace_text`,\n`write_file`, `sync`, `status`. The server is `latt mcp`, and it serves the git repository of the\ndirectory it runs in. So for most agents, add it to the project's own settings. `latt` must be on the\n`PATH` the agent sees.\n\nThe plugin brings the server and a skill that tells the model when to use it instead of Grep, Glob, Read, Edit and Write:\n\n```\n/plugin marketplace add julymetodiev/lantern-lattice\n/plugin install latt@latt\n```\n\nOr only the server, without the skill:\n\n```\nclaude mcp add --scope user latt -- latt mcp\n```\n\nThere is no plugin for these yet: add the server to the file shown. Claude Code is the one agent\nLantern Lattice has been used with so far, so these come from each agent's configuration format and are untested.\nIf one does not work, please [open an issue](https://github.com/julymetodiev/lantern-lattice/issues).\n\n| Agent | File in the project | \n|---|---|\n| Cursor | `.cursor/mcp.json` | \n| Gemini CLI | `.gemini/settings.json` | \n| Roo Code | `.roo/mcp.json` | \n\n```\n{ \"mcpServers\": { \"latt\": { \"command\": \"latt\", \"args\": [\"mcp\"] } } }\n```\n\nCodex, in `.codex/config.toml`:\n\n```\n[mcp_servers.latt]\ncommand = \"latt\"\nargs = [\"mcp\"]\n```\n\nVS Code, in `.vscode/mcp.json`:\n\n```\n{ \"servers\": { \"latt\": { \"type\": \"stdio\", \"command\": \"latt\", \"args\": [\"mcp\"] } } }\n```\n\nZed, in `.zed/settings.json`:\n\n```\n{ \"context_servers\": { \"latt\": { \"command\": \"latt\", \"args\": [\"mcp\"] } } }\n```\n\nOpenCode, in `opencode.json`:\n\n```\n{ \"mcp\": { \"latt\": { \"type\": \"local\", \"command\": [\"latt\", \"mcp\"], \"enabled\": true } } }\n```\n\nAny other MCP client: run `latt mcp` over stdio in the project's directory. To teach an agent when to\nuse the tools, put [the skill's text](https://github.com/julymetodiev/lantern-lattice/blob/main/integrations/claude-code/skills/latt-code/SKILL.md) in its\ninstructions, such as `AGENTS.md`.\n\n[Look at Lantern Lattice's own code this way](https://lanternlattice.com/), in a browser with WebGPU. On\nyour project:\n\n```\nlatt view\n```\n\nA browser opens on the whole graph, which changes as the code and the agents do. Symbols are placed by\ncrate and file. Zoom in, search, or choose a symbol to see what it calls, who calls it and its source.\nWhat a query or an edit touches lights up as it happens, blue for a read and red for an edit, so you can\nwatch an agent work. It only reads, loads nothing from the network, and listens on `127.0.0.1` with a\nprivate token in the address. It needs a browser with WebGPU; it has been tried in Chrome.\n\n`latt view --export DIR` writes the same page as files for any static host, with the graph as it is and\nthe source of every symbol, so use it only on code that may be public.\n\n- A call in `trace` is exact. A call Lantern Lattice cannot be sure of is left out, never guessed.\n- `[via trait, possible]` and`[implementation, possible]` : which implementation runs is decided at\nruntime.\n- `[as value, possible]` : a function named without being called, as in`.map(helper)` , or a constant\nholding one. It may run later, or never.\n- `[reads it]` : on a constant or static, a symbol that reads it.\n- `[uses it]` : on a type or field, a symbol that names it. Neither of these is a call, and they come\nonly for what you traced, not further along a chain.\n- `Partial:` analysis is still running, so calls may be missing. Ask again shortly.\n\nWithout `rust-analyzer`, `tsrs`, `pyrefly` or `scip-go`, or with `LATT_NO_SCIP=1`, only syntax rules apply, and `latt sync` says how many files that covers. In Rust they find about half the calls. In the others they link only\nfunctions of the same file or of an import in the project, and no methods.\n\nOn a copy of a project with 1311 Rust files and 20 260 symbols, on a laptop:\n\n| A query with nothing changed | 0.01 to 0.02 s | \n| A first answer after an edit | about 1 s (0.8 to 1.1 s), marked partial until complete | \n| A complete answer after the first edit | 8 to 12 s, then under 1 s | \n| From no daemon to a first answer | 3.2 to 3.8 s, with the index already written | \n| A first index | 1 to 2 minutes, at a peak of about 6 GB | \n| Memory | daemon about 370 MB idle; `rust-analyzer` about 2.2 GB while in use, stopped after 10 idle minutes | \n\nOn TypeScript, JavaScript and Python projects, on the same laptop, with the server stopped after 10 idle minutes too:\n\n|  | hono | nest | three.js | flask | requests | django | \n|---|---|---|---|---|---|---|\n| Symbols | 12 497 | 27 854 | 27 387 | 2 046 | 1 114 | 67 865 | \n| A first `latt sync` | 11 s | 48 s | 45 s | 2.1 s | 1.5 s | 65 s | \n| A new daemon with nothing changed | 1.0 s | 2.9 s | 4.2 s | 0.1 s | 0.1 s | 12 s | \n| Daemon after asking | 121 MB | 187 MB | 373 MB | 31 MB | 27 MB | 539 MB | \n| The server while in use | 963 MB | 1.6 GB | 603 MB | 233 MB | 178 MB | 614 MB | \n\nAn edit of a file most of a project imports asks about those files again: about 30 s on nest and django, with answers marked partial until then.\n\nOn Go projects, which work as Rust does, with an index and `gopls` for the files edited since:\n\n|  | hugo | kubernetes | \n|---|---|---|\n| Symbols | 19 065 | 221 808 | \n| A first `latt sync` , compiling what the project needs | 331 s | 513 s, and 14 GB in Go's build cache | \n| A new daemon with nothing changed | 1.3 to 1.9 s | 32 s | \n| A first answer after an edit | under 0.9 s | not measured | \n| Daemon after asking | 96 MB | 1.7 GB, and a peak of 4.7 GB while it reads the indexes | \n\nOn Lantern Lattice's own code, 1326 of 1326 calls between its functions match what `rust-analyzer` reports. The\nmeasurement says 1325, and the one difference is the measurement's: SCIP gives two functions in\ndifferent test binaries one symbol. Every entry of a hand-written list of traps in\n[crates/latt-resolve-ra/tests/fixture/expected.rs](https://github.com/julymetodiev/lantern-lattice/blob/main/crates/latt-resolve-ra/tests/fixture/expected.rs) holds too: same\nnames across types and crates, calls inside macros, trait methods. Scripts behind these numbers are in\n[scripts/bench](https://github.com/julymetodiev/lantern-lattice/blob/main/scripts/bench/README.md).\n\nWho uses what, against `rust-analyzer`'s find-references on the project above:\n\n|  | Compared | Both agree | Only Lantern Lattice | Only the server | \n|---|---|---|---|---|\n| Constants and statics | all 944 | 1814 | 46 | 13 | \n| Types and traits | a sample of 40 | 420 | 0 | 0 | \n| Variants | a sample of 40 | 295 | 0 | 0 | \n| Fields | a sample of 40 | 157 | 0 | 0 | \n\n\"Only Lantern Lattice\" is mostly code a server on this machine does not compile, as behind `#[cfg(...)]` or a\nfeature. A graph kept up to date through edits had 0 differences from a new index, on edits of comment\nlines in 3 files. These are measurements of one project, not a guarantee for yours.\n\nIn TypeScript and JavaScript, against `scip-typescript`, the targets inside a project agree at 70 998 of 72 557 places on hono, 57 401 of 58 894 on nest and 91 559 of 108 715 on three.js. Nearly all the rest are places only `tsrs` answers, or the start of a file against its first line; `scripts/bench/scip_compare.py` names each kind. After edits of comments in 2 to 3 files the graph had 0 differences from a clean start, on hono and nest. Agreement is not proof: these two tools can be wrong together.\n\nIn Python, against `scip-python`, the targets of places that name a function agree at 739 of 778 on flask and 414 of 434 on requests; on django that comparison did not finish. Most other differences read are local variables assigned twice, which give no edge. `pyrefly` gave the same targets at all 299 205 calls and attributes on django with files asked in reverse order, and after edits of 3 files on django the graph had 0 differences from a clean start. Agreement is not proof.\n\nKnown limits in Python: **`pyrefly` runs a project's `.venv/bin/python`** (see [SECURITY.md](https://github.com/julymetodiev/lantern-lattice/blob/main/SECURITY.md)). What has no type has no target, about a fifth of the calls in django's own code, and Lantern Lattice leaves those out and does not guess. A call through a variable, as `alias()` after `alias = plain`, or `cls()`, gives no edge. A call the source does not spell gives none either, as `a + b` running `__add__`. A class that fits a `Protocol` without naming it is not shown as implementing it. A package installed without a change to a lock file leaves saved answers stale. Environments other than a `.venv` at the top of a project are not measured.\n\nKnown limits in TypeScript and JavaScript: what has no type has no target, so plain JavaScript gets about two thirds of its calls (62% on express, 66% on three.js, against 91% and 94% on TypeScript projects). Lantern Lattice leaves the rest out and does not guess. `list[0].area()` on a union of types shows one member. pnpm and yarn workspaces are not measured, and `.vue` and `.svelte` files are left out. `tsrs` is a young beta.\n\nIn Go, against `gopls`, all 103 967 places on hugo that name something agree or fall in a known class: 50 369 at the same place, 36 908 outside the project in the same package, and the rest definitions, built-in names and files outside the build.\n\nKnown limits in Go: a first index compiles the project, as a build does, and a `go.mod` may make Go fetch the toolchain it names (see [SECURITY.md](https://github.com/julymetodiev/lantern-lattice/blob/main/SECURITY.md)). A file behind a build tag or for another system has syntax rules only, and `latt sync` says how many there are. A file over 500 KB is left out, in any language. `C.f()` is not linked. A call the source does not spell, as through `reflect`, has no edge, and neither has `f()` on a variable holding a function.\n\nKnown limits in Rust: a proc macro that fails silently while the index is written loses its calls;\none that says so is handled. A read and a write of a field are not told apart. A constant inside a format string has a read only in macros known to take one, and only if it is an item of the same module or imported by a project path: an attribute such as `#[error(\"{NAME}\")]`, a glob import or a renamed import leaves it out. A function named in an attribute, as in `#[serde(skip_serializing_if = \"name\")]`, has no call. Replacing a file by renaming breaks a hard link to it.\n\nLeft out on purpose for now: telling a read of a field from a write; keeping hard links when a file is written.\n\nRead [docs/ARCHITECTURE.md](https://github.com/julymetodiev/lantern-lattice/blob/main/docs/ARCHITECTURE.md), the specification, and\n[CONTRIBUTING.md](https://github.com/julymetodiev/lantern-lattice/blob/main/CONTRIBUTING.md). `scripts/check.sh` runs every check.\n\nSecurity: [SECURITY.md](https://github.com/julymetodiev/lantern-lattice/blob/main/SECURITY.md). Changes: [CHANGELOG.md](https://github.com/julymetodiev/lantern-lattice/blob/main/CHANGELOG.md). Releases:\n[RELEASING.md](https://github.com/julymetodiev/lantern-lattice/blob/main/RELEASING.md).", "url": "https://wpnews.pro/news/show-hn-lantern-lattice-the-call-graph-from-your-compiler-for-coding-agents", "canonical_source": "https://github.com/julymetodiev/lantern-lattice", "published_at": "2026-11-05 20:25:46+00:00", "updated_at": "2026-11-05 20:52:16.103318+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools", "agent-protocols"], "entities": ["Lantern Lattice", "July Metodiev", "rust-analyzer", "tsrs", "pyrefly", "scip-go", "gopls", "clangd"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/show-hn-lantern-lattice-the-call-graph-from-your-compiler-for-coding-agents", "markdown": "https://wpnews.pro/news/show-hn-lantern-lattice-the-call-graph-from-your-compiler-for-coding-agents.md", "text": "https://wpnews.pro/news/show-hn-lantern-lattice-the-call-graph-from-your-compiler-for-coding-agents.txt", "jsonld": "https://wpnews.pro/news/show-hn-lantern-lattice-the-call-graph-from-your-compiler-for-coding-agents.jsonld"}}