lainBLOCK
High-performance MCP server for AI coding agents: persistent knowledge graph, blast radius analysis, semantic search. Built in Rust.
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
A code graph for coding agents. Lain maps callers, dependencies, symbols, and Git co-changes, then exposes them through the Model Context Protocol (MCP). Before an agent edits a function, it can inspect what depends on it and how the rest of the code reaches it.
The demo traces a symbol across the bytes and tokio repositories. Agents query the same graph through MCP; the browser shows what Lain indexed and where an answer came from.
Ask your agent:
If I change validate_token, what breaks? Show me the call chain from entry to helper_a. Which files usually change with auth.rs? Is another agent already editing this code?
Lain answers with symbol names and source paths from a local index. The index stays on disk between runs and can cover more than one repository. It follows commits, not uncommitted edits: after committing, a re-index pass (lain reindex or a fresh session) brings it current — and answers that may miss recent work say so instead of going quiet. Semantic search is optional; graph queries work without an embedding model.
Install
Using the release installer:
curl -fsSL https://raw.githubusercontent.com/spuentesp/lain/main/install.sh | bash source ~/.zshrc # or ~/.bashrc lain --version
Rust 1.89 or newer is only needed when building from source. The quickstart covers non-interactive installation and the optional local embedding model.
Connect an agent
Run one setup command from the repository you want Lain to index:
lain setup --agent claude-code lain setup --agent codex lain setup --agent cursor lain setup --agent vscode lain setup --agent continue
Use lain setup --agent generic for another MCP host. It writes this project-level .mcp.json, with the absolute path of the lain binary that ran setup as command:
{
"mcpServers": {
"lain": {
"command": "/home/you/.local/bin/lain",
19bc35bd911bOBSERVED · 2026-10-11Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add lain-mcp -- npx -y @spuentesp/[email protected]
Exposed tools (26)
22 read · 4 write · 0 destructive.
| Tool | Risk | Description |
|---|---|---|
assess_change | read | Impact assessment |
check_binding | read | Validate a proposed consumer→endpoint link and emit the YAML for |
debug_sleep | read | Sleep for the given number of seconds (useful for testing job infrastructure). |
diff_contracts | read | Provider and consumer changes between two snapshots, classified, with impact paths and coverage (§12). |
find_anchors | read | Find key files |
get_agent_strategy | read | Return the recommended tool sequence and quick-reference for working with Lain. |
get_capabilities | read | Return the current repository capability states, freshness, and indexing progress. Warming states are retryable. |
get_contract | read | Providers, schema fields, and bound consumers for one endpoint (§12). |
get_coverage | read | What the view saw and what it could not resolve (§9.7). With an |
get_health | write | Return server health, node/edge counts, last enriched commit, and language-server availability. |
get_job_status | read | Get the current status and output of a previously-spawned background job. |
get_service | read | Consumers of one service: endpoints, calling code, fields used, and the |
get_snapshot | read | Read a snapshot |
install_language_server | write | Install one or more language servers. Pass a single |
list_contracts | read | Endpoints the federation exposes, filterable by service, repo, and kind (http/topic). |
list_packages | read | The skill menu: every capability package with its pitch, level, why it is off by default, and its tools. |
list_services | read | Every service in the federation with its repo, paths, endpoint count and consumer counts. Works on |
list_unresolved | read | Every ambiguous and unresolved consumer with the candidate endpoints that could match (§9.7). |
load_package | read | Opt a package (verify, arch, raw, notes, session, social, ops) into this session |
prepare_snapshot | write | Pin every repo to a commit, index once, reuse the result. Same inputs after ref resolution produce the same id (§8.4); |
random_extension | read | Custom |
read_source | read | A bounded line range of a file at a view |
register_job_webhook | read | Register a webhook URL to receive notifications when background jobs complete. |
resolve_evidence | write | Check refs exist at their commit; return context. Bad refs are |
trace_impact | read | Impact paths from an endpoint, field, or symbol (§5.2 + §12). Exactly one of endpoint/field/symbol is required. |
understand_repository | read | Semantic map |
Trust audit
BLOCKgrade D · trust 69/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | PASS |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (13 observation(s))
- Network
- declared (3 observation(s))
- Shell
- declared (3 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- none-found
Findings (25)
def load_blob(blob):\n return pickle.loads(blob)\n",
const PATTERNS: &[&str] = &[".env", ".npmrc", ".pypirc", ".netrc"];
if basename_lower.starts_with("id_rsa")|| basename_lower.starts_with("id_ecdsa")|| basename_lower.starts_with("id_ed25519")"id_rsa",
sanitize_agent_name("../../../../etc/passwd"),assert_eq!(canonical_file("../../etc/passwd"), "");pub const BLAST_RADIUS_HTML: &str = include_str!("../../ui/blast-radius.html");pub const COUPLING_HTML: &str = include_str!("../../ui/coupling.html");pub const CALL_CHAIN_HTML: &str = include_str!("../../ui/call-chain.html");if ! curl -fsS --max-time 600 -o "$RESP" -X POST http://127.0.0.1:9999/mcp \
curl -fsS --max-time 900 -X POST http://127.0.0.1:9999/mcp \
if CAPS_RESP=$(curl -fsS --max-time 30 -X POST http://127.0.0.1:9999/mcp \
BR_TEXT=$(curl -fsS --max-time 30 -X POST http://127.0.0.1:9999/mcp \
ANN_RESP=$(curl -fsS --max-time 30 -X POST http://127.0.0.1:9999/mcp \
statement = json.loads(base64.b64decode(envelope['payload'], validate=True))
"N", "Graph (μs)", "Naive Grep (μs)", "Speedup", "Nodes"
"N", "Graph (μs)", "Naive LSP (μs)", "Speedup", "Affected"
"N", "Graph (μs)", "Multi-File (μs)", "Speedup", "Related"
"N", "Graph (μs)", "Naive (μs)", "Speedup"
"N", "Find (μs)", "Traverse (μs)", "Total"
pytest, requests
playwright
a `curl -X POST http://localhost:9999/mcp ...` snippet to the clipboard).
Gates applied: no_behavioural_pass.
19bc35bd911bfull audit observations/trust-audit/mcp-server/spuentesp__lain.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-11 | 19bc35bd911b | BLOCK | D | 69 | first audit |
Questions
What is the lain MCP server?
High-performance MCP server for AI coding agents: persistent knowledge graph, blast radius analysis, semantic search. Built in Rust.
What tools does lain expose?
26 in total: 22 read-only, 4 that write, and 0 that can delete or overwrite. Every one is listed on this page with its risk.
Is lain safe to connect to an agent?
No — not without reading the findings first. The audit graded it D (69/100) and found 6 critical or high issues in the source. Each one is listed on this page with the file and line it is on.
What credentials does lain need?
It reads SECRET_KEY from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does lain run?
It speaks stdio, so it runs as a local process your client starts. It is published on npm as @spuentesp/lain-mcp at 0.9.0.
How current is this page?
The grade is for one exact copy of the source (19bc35bd911b), read on 2026-10-11. The repository is watched and re-audited when it changes.