{"slug": "enola-mcp-architectural-snapshot-server-and-knowledge-graph", "title": "Enola – MCP Architectural Snapshot Server and Knowledge Graph", "summary": "Enola Labs released enola, a local Model Context Protocol server that builds a deterministic architectural graph of codebases from source code for AI coding agents. The tool extracts precise structural models — modules, types, routes, dependencies — across 16+ languages and frameworks, eliminating guesswork and reducing token waste in AI-assisted development. enola runs as a pipeline that resolves syntax trees into a queryable cross-file, cross-repo graph, supporting agents like Claude Code, Cursor, and GitHub Copilot.", "body_md": "**A deterministic structural model of your codebase for AI coding agents — your real architecture, extracted from source, not guessed.**\n\nenola is a local [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) server. Point it at one or more repositories and it builds a precise graph of your code's architecture — modules, types, routes, dependencies, and how they all connect — straight from your source. It then exposes tools your AI agent can use to read, traverse, query, and reason over that structure. So before your agent writes a line of code, it already knows the shape of the thing it's editing.\n\n**1. Install**\n\n```\ncurl -fsSL https://raw.githubusercontent.com/enola-labs/enola/main/install.sh | sh\nexport PATH=\"$HOME/.local/bin:$PATH\"   # if not already on PATH\n```\n\n**2. Connect to your agent**\n\nClaude Code:\n\n```\nclaude mcp add enola enola\n```\n\nCursor (add to `mcp.json`\n\n):\n\n```\n{\n  \"mcpServers\": {\n    \"enola\": { \"command\": \"enola\" }\n  }\n}\n```\n\nGitHub Copilot / VS Code (add to `.vscode/mcp.json`\n\n):\n\n```\n{\n  \"servers\": {\n    \"enola\": { \"command\": \"enola\" }\n  }\n}\n```\n\n**3. Ask it to map your project**\n\n\"Generate an architectural snapshot of /path/to/my/project\"\n\nDone. Your agent now has a precise structural map of your code. For configuration options, multi-repo setup, and what to ask next, see [Quick start](#quick-start) below.\n\n**Supported languages:** Go · JavaScript · TypeScript · Python · Java · Kotlin · Swift · Ruby · Rust · C · C++ · PHP · Vue · Svelte · OpenAPI · gRPC — with framework awareness (Next.js, Nuxt, SvelteKit, FastAPI, Django, Spring, Rails, Laravel, Symfony, SwiftUI, Jetpack Compose, WordPress, Kafka, …)\n\nAI coding agents are powerful, but they're non-deterministic. On every task they re-discover your codebase from scratch — grepping, opening files, inferring how things fit together — and they get it subtly wrong often enough to matter. That guessing costs you **time** (wrong turns, re-prompts) and **tokens** (re-reading the same files, every session).\n\nenola removes the guessing from the part that should never be guessed: the structure.\n\nIt gives your agent a **deterministic structural model** — a structural architecture graph of your code's real types and relationships, built by parsers and graph algorithms, not by a language model. The structure is *extracted* from your source, not *summarized* from it; these are facts, not notes. Run it twice on the same commit and you get the same answer, every run. The agent starts from facts instead of assumptions.\n\nThe result is the difference between *vibe coding* — prompt, hope, fix — and **AI-augmented engineering**: fewer wrong turns, fewer tokens burned, and work you can reproduce. enola adds determinism where AI lacks it, and your agent spends its intelligence on the actual problem instead of re-learning your repo.\n\nenola is a\n\nfirst step, not a replacement. It runsbeforeyour agent explores, so it knows where to look and what connects to what. It doesn't replace grep, file reading, or code search — it makes them precise.\n\nFair question — plenty of tools parse an AST. enola does too. But parsing is the *entry point*, not the product: it's **stage 2 of an 8-stage pipeline** ([ARCHITECTURE.md → The pipeline](/enola-labs/enola/blob/main/ARCHITECTURE.md#the-pipeline)). A tree-sitter grammar or an LSP hands you a syntax tree per file; enola treats that as raw material and resolves it into a queryable graph across your whole system.\n\n| A plain AST / tree-sitter / LSP tool gives you… | enola gives you… |\n|---|---|\n| a syntax tree, one file at a time | a typed, directed graph resolved across files, languages, and repos |\n`foo.bar()` as a call to an unknown token |\n`foo.bar()` resolved to the exact declaring symbol — through dispatch, inheritance, and imports |\n| edges exactly as written in source | edges made semantically complete — synthetic `has_method` , module→import bridges, cross-repo links |\n| \"find where this text appears\" | \"what transitively depends on this?\" — answered by graph traversal, with accurate totals |\n| no way to tell a leaf from a blind spot | a genuine leaf service told apart from a coverage gap |\n| text you re-grep and re-infer every session | a byte-identical, content-fingerprinted snapshot |\n\nThe gap a parser can't cross is *resolution*. A parser sees `foo.bar()`\n\nas tokens; enola resolves it to the symbol that actually declares `bar`\n\n, across every dispatch mechanism real code uses — Ruby `send`\n\n/`public_send`\n\n, Swift inherited-method chains, Kotlin callable references, Python absolute-import call edges, Java fully-qualified-name indexing — and then links per-repo graphs so a web client's HTTP or gRPC call resolves all the way to the backend route that serves it. That's why `traverse`\n\n, `impact_analysis`\n\n, and `find_path`\n\nreturn the *exact* dependent set instead of grep hits: the graph builder adds edges to make traversals *\"semantically complete rather than literally what each parser emitted.\"*\n\nAnd it holds itself to that standard. enola reproduces its output **to the byte** — the snapshot ID is a content fingerprint, not a random UUID — and a large share of its engineering goes into catching what a naive parser gets subtly wrong: two apps that merely *name* the same type aren't fused into a false dependency, and a service with unresolved outbound calls is reported as a coverage gap, not a phantom leaf. The graph is *derived, never guessed*; run it twice on the same commit and it's identical, byte for byte ([ARCHITECTURE.md → Determinism](/enola-labs/enola/blob/main/ARCHITECTURE.md#determinism--incremental-updates)).\n\nUnder the hood, enola models your codebase as a **graph of architectural types — which we call kinds — and the relations between them.** That's the whole concept: not a magic \"knowledge graph,\" just a deeply technical, structural model of what your code actually contains.\n\nThe **kinds** (the nodes):\n\n**module**— a package or directory** symbol**— a function, method, struct, interface, type, class, or constant** route**— an HTTP/API endpoint** storage**— a database table, data store, or messaging topic** dependency**— an import relationship** service**— a whole repository (used when you analyze several at once)\n\nThe **relations** (the edges) connect them: *declares*, *imports*, *calls*, *implements*, *depends_on*, and more. Because the edges are typed and directed, the graph is *queryable*, not merely searchable — you compute over it. On top of it, enola builds a small set of tools your agent can call to answer real structural questions with exact answers.\n\nGetting to that graph is where the pipeline earns its keep. AST parsing is one stage; the rest is what makes the result queryable: **parse → normalize into the typed fact model → link across repos (with 2+ loaded) → build a bidirectional graph index with synthetic edges → run deterministic explainers → emit a provenance receipt** ([ARCHITECTURE.md → The pipeline](/enola-labs/enola/blob/main/ARCHITECTURE.md#the-pipeline)). The explainers are **real graph algorithms, not regex heuristics** — Tarjan's SCC for dependency cycles, cycle-safe longest-path for dependency depth, statistical (mean + 2σ) outlier tests for god-classes, hotspots, and complexity — and each finding carries a confidence score, where `1.0`\n\nmeans a structural fact and anything below is a flagged heuristic ([ARCHITECTURE.md → Insights (explainers)](/enola-labs/enola/blob/main/ARCHITECTURE.md#insights-explainers)).\n\nFor the full mental model and internals, see ** ARCHITECTURE.md**.\n\nEveryone above the keyboard needs the same thing enola produces: a structural map of the system that's *actually correct*. Because the graph is deterministic and derived from source, an agent can turn it into an always-accurate architecture diagram on demand — a mermaid module graph, a cross-repo dependency map — and it matches the code every time, and again next quarter, byte for byte. (enola doesn't draw the diagram; it hands your agent the facts to draw it from, so the picture is never the stale one on the wiki.)\n\n**Developers pairing with an AI coding agent**— Claude Code, Cursor, Copilot, Opencode, or any MCP-compatible tool. The agent starts from your real structure instead of re-guessing it every task.**Teams working across multiple repos**— a backend, a web frontend, a mobile app. enola links them into one cross-repo graph so an agent can follow a call from the web client all the way into the service that answers it — including the*asynchronous*hop a call graph can't see, where one service consumes the Kafka events another produces. And because that's a real graph rather than a fixed set of features, questions you'd otherwise reach for a dedicated tool to answer become plain queries over it —*which of the backend's endpoints does no client app call?*among them, a cleanup shortlist derived from the same client→server links (verify against callers outside the snapshot — cron jobs, webhooks, third-party consumers — before deleting).**Anyone about to refactor**— and wanting to know the blast radius*before*touching code.**Architects**— the structural view you usually maintain by hand, computed from the code instead of a diagram that drifts out of date: dependency cycles, layer violations, call-graph hotspots, cross-repo coupling, and dependency depth — plus a module/dependency diagram an agent regenerates from the current commit, so the picture stays honest.**Engineering leaders — CTOs, VPs, and managers**— a trustworthy picture of a codebase's shape for planning, onboarding, and risk. Deterministic signals (cycles, hotspots, coupling) instead of gut feel, a new-hire tour or a deck diagram that matches reality, and a map that's reproducible run to run — so two people looking at it see the same thing.\n\nThe workflow is simple: **generate the snapshot once, then ask.** These aren't text lookups — each tool *computes over the graph*: `traverse`\n\nwalks reachability, `find_path`\n\nfinds the shortest chain between two points, `impact_analysis`\n\ntakes the transitive reverse closure. After the snapshot, your agent has these tools on top of the graph:\n\n| Tool | The question it answers |\n|---|---|\n`generate_snapshot` |\n\"Snapshot this repo.\" Build or refresh the graph. Run it first; use `append` to add more repos. |\n`explore` |\n\"What's in this module/file/symbol, and what touches it?\" A guided tour. |\n`query_facts` |\n\"List exactly these.\" Every route, every interface, every external dependency. |\n`query_insights` |\n\"What did the analysis find?\" Fetch the computed findings — unused routes, cycles, god-classes — instead of re-deriving them. |\n`show_symbol` |\n\"Show me the code.\" Jump straight to a symbol's source. |\n`traverse` |\n\"What does X depend on?\" / \"What depends on X?\" Walk the graph. |\n`find_path` |\n\"How does A reach B?\" The call or dependency chain between two points. |\n`impact_analysis` |\n\"If I change X, what breaks?\" The blast radius of a change. |\n`coverage_report` |\n\"Which cross-repo edges did enola resolve vs. miss?\" Tell a genuine leaf service from a coverage gap. |\n`set_baseline` |\n\"Remember the architecture as it is now.\" Pin a baseline before you start editing. |\n`diff_snapshot` |\n\"What did my change actually do?\" The architectural delta vs. the baseline — new findings, new coupling, added/removed symbols. Warns if the two snapshots aren't comparable. |\n`snapshot_receipt` |\n\"What was this graph generated over, and how complete is it?\" Provenance (version, git ref + dirty, snapshot ID, output hashes) plus extraction-quality metrics. |\n`compare_receipts` |\n\"Are these two snapshots even comparable?\" A comparability verdict + metric deltas — the gate before trusting a diff, and a signal for improving coverage. |\n\n** impact_analysis is the one to know.** Before a refactor, it computes the full set of code that transitively depends on what you're about to change — grouped by how many hops away it is, and aware of cross-repo dependencies. Instead of your agent\n\n*guessing*what a change might affect (and missing things), it gets the exact dependent set. That's determinism turned into a concrete payoff: safer changes, planned in the right order, the first time.\n\n** diff_snapshot closes the loop on the edit itself.** Where\n\n`impact_analysis`\n\nplans a change, `diff_snapshot`\n\nverifies it: pin a baseline (`set_baseline`\n\n), make your edits, re-snapshot, and ask what changed. It's a **delta, not a linter**— it reports only what\n\n*moved*(findings that newly appeared or were resolved, new/removed coupling, added/removed symbols) and stays silent about pre-existing state, so a pattern that was already there before\n\n*and*after never fires. Instead of re-reading files to confirm the agent built what it claimed, you get a deterministic answer:\n\n`generate_snapshot → set_baseline → edit → generate_snapshot → diff_snapshot`\n\n.See ** ARCHITECTURE.md** for every tool's full parameters.\n\nThe examples below ask different models to explain the authentication and authorization flow across **three repositories** — a web UI client, a backend, and a custom auth provider — using the enola snapshot as context.\n\nGrab a prebuilt binary — no Go toolchain or C compiler required:\n\n```\ncurl -fsSL https://raw.githubusercontent.com/enola-labs/enola/main/install.sh | sh\n```\n\nThis installs `enola`\n\nto `~/.local/bin`\n\n. If that's not on your `PATH`\n\n, add it:\n\n```\nexport PATH=\"$HOME/.local/bin:$PATH\"\n```\n\nBinaries are published for Linux, macOS (amd64/arm64), and Windows (amd64). You can also download a specific build from the [Releases page](https://github.com/enola-labs/enola/releases), or [build from source](#build-from-source).\n\nOnce installed, update to the latest release in place:\n\n```\nenola upgrade\n```\n\nThis downloads the newest build for your platform, verifies its checksum, and replaces the running binary. If enola is installed somewhere your user can't write, re-run with elevated permissions or re-run the install script above.\n\nBecause your agent launches enola as a long-lived MCP server process, an upgrade only takes effect once that process restarts — reconnect the MCP server so it picks up the new binary:\n\n**Claude Code**— restart the session, or re-register with`claude mcp remove enola && claude mcp add enola enola`\n\n.**Cursor**— toggle the enola server off and back on in** Settings → MCP**(or reload the window).** GitHub Copilot (VS Code)**— restart the server from the`.vscode/mcp.json`\n\neditor (the**Restart** CodeLens above the server entry), or reload the window.\n\n**enola needs no config file.** Every setting has a built-in default, so out of the box it indexes the current repo with all extractors enabled and writes to `.enola/`\n\n. A config file (`mcp-arch.yaml`\n\n) only *overrides* those defaults — it never adds capability you'd otherwise lack. When enola can't find one it simply prints `warning: …, using defaults`\n\nand carries on.\n\nThe install script installs **only the binary**, by design — it does not place a config file. Grab the bundled one from the repo whenever you want to customize (tune the `ignore`\n\nglobs, pick a subset of extractors, change the output dir, …):\n\n```\ncurl -fsSL https://raw.githubusercontent.com/enola-labs/enola/main/mcp-arch.yaml -o mcp-arch.yaml\n```\n\nThe [ examples/](/enola-labs/enola/blob/main/examples) directory has ready-made per-language and multi-repo starting points, and\n\n[documents every option. For the full field reference and defaults, see](/enola-labs/enola/blob/main/examples/full.yaml)\n\n`examples/full.yaml`\n\n**.**\n\n[ARCHITECTURE.md → Configuration](/enola-labs/enola/blob/main/ARCHITECTURE.md#configuration)**Claude Code** — register enola as an MCP server with one command. This assumes the `enola`\n\nbinary is on your `PATH`\n\n(the install script above puts it in `~/.local/bin`\n\n):\n\n```\nclaude mcp add enola enola\n```\n\nThe shape is `claude mcp add <name> <command> [args…]`\n\n: the first `enola`\n\nnames the server, the second is the binary. The trailing config path is **optional** — omit it (as above) to run on built-in defaults, or pass one to override them:\n\n```\nclaude mcp add enola enola /path/to/enola/mcp-arch.yaml\n```\n\nWhen you do pass a config, its `repo:`\n\nis only the *default* repository — you can still snapshot any repo by passing `repo_path`\n\nto `generate_snapshot`\n\n. Verify it registered with `claude mcp list`\n\n, then start Claude Code and ask it to generate a snapshot.\n\n**Cursor / other MCP clients** — add enola to your client's MCP configuration. For example, in Cursor's `mcp.json`\n\n(the config path in `args`\n\nis optional — drop it to use defaults):\n\n```\n{\n  \"mcpServers\": {\n    \"enola\": {\n      \"command\": \"enola\",\n      \"args\": [\"/path/to/enola/mcp-arch.yaml\"]\n    }\n  }\n}\n```\n\n**GitHub Copilot (VS Code)** — add enola to `.vscode/mcp.json`\n\nin your workspace (or your user-level MCP config via **MCP: Open User Configuration**). Note the top-level key is `servers`\n\n(not `mcpServers`\n\n), and the config path in `args`\n\nis optional — drop it to use defaults:\n\n```\n{\n  \"servers\": {\n    \"enola\": {\n      \"command\": \"enola\",\n      \"args\": [\"/path/to/enola/mcp-arch.yaml\"]\n    }\n  }\n}\n```\n\nOr add it from the command line: `code --add-mcp \"{\\\"name\\\":\\\"enola\\\",\\\"command\\\":\\\"enola\\\"}\"`\n\n. Then open a project and ask Copilot to generate a snapshot.\n\nOpen a project and ask your agent to map it:\n\n\"Generate an architectural snapshot of /path/to/my/project\"\n\nThat's it. The snapshot takes milliseconds even on large repos, and your agent now has the tools above plus a ready-to-read summary at `.enola/llm_context.md`\n\n. From here, just ask your questions naturally:\n\n\"I just joined this project — based on the snapshot, give me a tour: the main modules, how they relate, and where to start reading.\"\n\n\"I need to add an API endpoint for user preferences. Which packages should I touch, and in what order?\"\n\n\"Are there cyclic dependencies or layer violations I should know about before refactoring?\"\n\n\"Where are the architectural risks — god classes with high fan-in, call-graph hotspots, overly complex functions, or modules buried deep in the dependency chain?\"\n\n\"What would break if I refactor\n\n`internal/server`\n\n? Show me the impact analysis.\"\n\nWorking across several repos? Generate the first, then add the rest with append mode — enola links them into one cross-repo graph:\n\n\"Generate a snapshot of /path/to/go-service with append mode\"\n\n\"If I change the auth service, which other services are impacted?\"\n\n\"Which of my backend's endpoints aren't called by any of the client apps? (Ask via\n\n`query_insights(explainer='unused-routes')`\n\n— cleanup candidates, but check for callers outside these repos first.)\"\n\nWhen you snapshot a *different* repo without `append`\n\n, enola assumes you're extending the set and auto-appends it — handy when you forgot `append`\n\non repo #2. If you've actually **moved to another project** and want a clean single-repo snapshot instead, ask for a fresh one (`fresh=true`\n\n) so the old repos are discarded rather than merged in.\n\n**Regenerate after major changes** so the snapshot stays current. Refreshes are fast: enola caches each language's facts and re-parses a language only when one of its files (or a shared config like `package.json`\n\n) actually changed, reusing the rest.\n\nVery large repositories (e.g. the Linux kernel).The first, cold index of a huge repo can take a minute or more and may exceed your MCP client's per-tool-call timeout, surfacing as`MCP error -32001: Request timed out`\n\n. The snapshot usually still finishes and is cached server-side — but to avoid the error, either:\n\nRaise your MCP client's tool-call timeout.In Claude Code, set the`MCP_TOOL_TIMEOUT`\n\nenvironment variable (milliseconds) before launching, e.g.`MCP_TOOL_TIMEOUT=600000`\n\n.Pre-generate from the shell once, then start the server: run`enola --generate <config-pointing-at-the-repo>`\n\n(writes`.enola/`\n\n), after which the MCP server auto-loads the cached snapshot on startup and later`generate_snapshot`\n\ncalls reuse the extractor cache (only changed files are re-parsed), so they return quickly.\n\n| Language | Detected by |\n|---|---|\n| Go | `go.mod` (gorilla/mux + chi route composition / gRPC clients / Kafka topics aware) |\n| Java | `pom.xml` (Maven) or `.java` sources (Spring routes / JPA / Lombok DI / Dubbo SPI aware) |\n| JavaScript | `tsconfig.json` / `package.json` with TypeScript (parsed by the TypeScript extractor) |\n| TypeScript | `tsconfig.json` / `package.json` with TypeScript (Next.js & monorepo aware) |\n| Vue | `package.json` with `vue` dependency (Nuxt / Vue Router / Composition API aware) |\n| Svelte | `package.json` with `svelte` dependency (SvelteKit routing / `$lib` alias aware) |\n| Python | `pyproject.toml` , `requirements.txt` , `setup.py` , … (FastAPI / Django / SQLAlchemy aware) |\n| Kotlin | `build.gradle(.kts)` with Kotlin/Android (Compose / Hilt / Room aware) |\n| Swift | `Package.swift` , `.xcodeproj` , `.xcworkspace` (SwiftUI / UIKit aware) |\n| Ruby | `Gemfile` (Rails / ActiveRecord / Packwerk aware) |\n| Rust | `Cargo.toml` (workspace or single crate; crate/module/`impl` /trait aware; Axum route DSL aware) |\n| C / C++ | `.c` /`.h` (tree-sitter-c) or `.cpp` /`.hpp` /… (tree-sitter-cpp), or `CMakeLists.txt` /`Makefile` + header (per-fact `language` , header/source method merging, namespaces, templates) |\n| PHP | `composer.json` , WordPress markers, or any `.php` source (WordPress / Laravel / Symfony route + outbound HTTP-client aware) |\n| OpenAPI | any spec with an `openapi:` / `swagger:` key |\n| gRPC | any `.proto` file (proto services → routes; TypeScript gRPC-web client calls detected) |\n\nFramework- and platform-specific detection for each language is described in ** ARCHITECTURE.md → Supported languages**.\n\nPython, Ruby, PHP, and Rust are parsed with tree-sitter and contribute call and dependency edges to the graph, so\n\n`traverse`\n\n,`find_path`\n\n, and`impact_analysis`\n\nreach into them — not just modules and routes.\n\nPrerequisites: **Go 1.25+** and a **C compiler** (for the tree-sitter bindings).\n\n```\ngo build -o enola ./cmd/enola   # or: go install ./cmd/enola\n```\n\nTo run a one-shot snapshot without starting the MCP server:\n\n```\nenola --generate [config_path]   # config_path is optional; defaults to mcp-arch.yaml, falling back to built-in defaults if absent\n```\n\nArtifacts are written to the configured `output.dir`\n\n(default `.enola/`\n\n). The config file is optional — see ** ARCHITECTURE.md → Configuration** for the full field reference and defaults.\n\n`enola --explain [repo_path]`\n\nis a one-shot mode that generates a snapshot, computes statistics over the fact graph, and prints a human-readable report to stdout — no MCP server started, no artifacts written to `.enola/`\n\n.\n\n**When to use it:**\n\n- New contributor getting a first orientation — module count, architecture pattern, hottest packages.\n- Pre-refactor sanity check — cycles, layer violations, blast radius of top modules.\n- Quick audit without spinning up an AI agent.\n\n```\n# Use the config in the current directory (mcp-arch.yaml)\nenola --explain\n\n# Analyze a specific repository path\nenola --explain /path/to/repo\n```\n\n**The report covers eight sections:**\n\n**Overview**— path, analysis time, active languages, total fact count** Architectural kinds**— counts of modules, symbols, routes, storage, dependencies, services** Symbol breakdown**— functions, methods, structs, interfaces, and other kinds** API & data surface**— route count broken down by HTTP method, plus storage count** Dependencies**— external, internal, and stdlib import counts** Architecture**— detected pattern with confidence, cyclic dependencies, layer violations, cross-repo edges** Impact analysis (hotspots)**— top modules ranked by fan-in + fan-out coupling, with criticality tier and blast radius** Code health**— per-explainer findings with their top offenders: god classes (high fan-in symbols), call-graph hotspots, deep dependency chains, large public surfaces, and complexity outliers\n\nEvery finding carries a confidence score, and it means something exact: `1.0`\n\nis a structural fact (a cycle exists; an export ratio measured), while anything below is a flagged heuristic for you to review (a god class is a statistical fan-in outlier, not a rule). The analyses are computed by graph algorithms — Tarjan's SCC for cycles, longest-path for dependency depth, mean+2σ outlier tests for the rest — so the same commit yields the same report.\n\nHere's the actual report for [Apache Airflow](https://github.com/apache/airflow) — a large polyglot codebase (Python, Java, TypeScript, gRPC, and OpenAPI specs) analyzed in a single pass, 122,257 facts in ~2s (extraction parses files in parallel across cores):\n\n```\n════════════════════════════════════════════════════════════\n Repository explanation: apache/airflow\n════════════════════════════════════════════════════════════\n\nOverview\n  Generated:           2026-07-07T20:51:51Z\n  Analysis time:       2.100326959s\n  Languages:           python, typescript, java, grpc\n  Total facts:         122257\n\nArchitectural kinds\n  module                   2500\n  symbol                  64148\n  route                    6426\n  storage                    64\n  dependency              44371\n\nSymbol breakdown\n  method                  40976\n  function                12409\n  class                    8583\n  type                     1393\n  variable                  732\n  interface                  37\n  struct                     10\n  enum                        6\n  constant                    2\n\nAPI & data surface\n  routes                   6426\n    PATCH                  6038\n    GET                     247\n    POST                     74\n    DELETE                   46\n    PUT                      20\n    HEAD                      1\n  storage                    64\n\nDependencies\n  internal                20905\n  stdlib                  12858\n  external                10607\n  unclassified                1\n\nArchitecture\n  Pattern:             (none detected)\n  cyclic dependencies        27\n  layer violations            0\n\nImpact analysis (hotspots)\n  coupled modules           915\n    high criticality        547\n    medium criticality      368\n  Top hotspots (by coupling):\n    module                            fan-in  fan-out crit     blast radius\n    airflow-core/src/airflow/models     1661      380 high     66716\n    devel-common/src/tests_common/t…    1496      161 high     39422\n    providers/common/compat/src/air…    1295        2 high     59376\n    airflow-core/src/airflow/utils      1124      132 high     68653\n    airflow-core/src/airflow            1172       71 high     71697\n    providers/common/compat/tests/u…     776        0 high     65589\n    providers/google/src/airflow/pr…     327      329 high     14701\n    task-sdk/src/airflow/sdk             591       15 high     64026\n\nCode health\n  god classes (high fan-in)    249\n    chart/tests/chart_utils/helm_template_gener… 1193 dependents\n    devel-common/src/tests_common/test_utils/co… 557 dependents\n    devel-common/src/tests_common/test_utils/sy… 476 dependents\n    dev/breeze/src/airflow_breeze/utils/console… 376 dependents\n    airflow-core/src/airflow/utils/session.crea… 288 dependents\n  call-graph hotspots       150\n    chart/tests/chart_utils/helm_template_gener… fan-in 1193 / out 7\n    devel-common/src/tests_common/test_utils/co… fan-in 557 / out 10\n    task-sdk/src/airflow/sdk/execution_time/tas… fan-in 101 / out 37\n    airflow-core/src/airflow/utils/session.crea… fan-in 288 / out 6\n    providers/hashicorp/src/airflow/providers/h… fan-in 76 / out 21\n  deep dependency chains     10\n    chart/docs                                   depth 196\n    dev/breeze/tests                             depth 196\n    dev/breeze/tests/integration_tests           depth 196\n    scripts/tools                                depth 196\n    airflow-core/tests/unit/api_fastapi/core_ap… depth 195\n  large public surfaces      20\n    airflow-core/src/airflow/ui/openapi-gen/que… 864/864 (100%)\n    airflow-core/src/airflow/ui/openapi-gen/req… 720/720 (100%)\n    task-sdk/src/airflow/sdk/execution_time/com… 119/129 (92%)\n    providers/edge3/src/airflow/providers/edge3… 100/100 (100%)\n    task-sdk/src/airflow/sdk/definitions/mapped… 100/111 (90%)\n  complexity outliers        15\n    airflow-core/src/airflow/jobs/scheduler_job… complexity 69\n    task-sdk/src/airflow/sdk/execution_time/sup… complexity 61\n    airflow-core/src/airflow/ui/src/pages/DagsL… complexity 54\n    airflow-core/src/airflow/ui/src/hooks.useDa… complexity 53\n    dev/breeze/src/airflow_breeze/commands/ci_c… complexity 52\n```\n\nNo artifacts are written; `.enola/`\n\nis not touched. For a persistent snapshot with agent-readable output, use `--generate`\n\nor the MCP server.\n\nFor interactive per-module blast-radius queries with configurable depth, see the `impact_analysis`\n\ntool reference in ** ARCHITECTURE.md → The tools**.\n\nRun `enola --help`\n\nfor the full text. With no flags, enola starts the MCP server on stdio.\n\n| Flag | What it does |\n|---|---|\n`--generate [config_path]` |\nGenerate a snapshot and exit — no MCP server. Artifacts go to `output.dir` (default `.enola/` ). |\n`--explain [repo_path]` |\nPrint the statistics report above and exit. Read-only: nothing is written to `.enola/` . |\n`--list` |\nList the MCP tools this build serves, with one-line summaries. |\n`--status` |\nList every enola server running right now — PID, repos, uptime, calls, dashboard URL — plus per-tool call counts and an estimate of the time and context those calls saved. |\n`--status --all` |\nThe same usage, broken down per repository. |\n`--no-dashboard` |\nStart the MCP server without the localhost dashboard. |\n`--version` |\nPrint the build version. |\n`--help` , `-h` |\nShow usage. |\n`upgrade` |\nDownload and install the latest release over the running binary. |\n\nUsage counters are recorded per repository under `~/.enola/usage/`\n\n, so they survive both server restarts and deleting a repo's `.enola/`\n\ndirectory — and `--status`\n\nworks from any directory, not just a snapshotted one. The value estimate is exactly that: a static per-tool model of how many manual lookups (open a file, grep, read) one call replaces, converted to time and tokens.\n\nStarting the MCP server also starts a **read-only dashboard** on a free loopback port (`127.0.0.1`\n\n), printed to stderr on startup — run `enola --status`\n\nwhile the server is up to get the URL again. It refreshes every 30 seconds and shows, in one page:\n\n**this server**— its PID, binary, uptime, the repos*it*has loaded, and the directory it was launched from;**every enola server running right now**, with a link to each one's dashboard, so you can switch between them;- the same activity and value data as\n`--status`\n\n, split into what this server has served and the lifetime total across all of them; - the\n**snapshot receipt**— snapshot ID, enola version, git ref, extractors, fact/insight counts; - the\n**graph receipt**— the repos in this server's graph, and clickable counters listing the services and cross-repo edges (the edges also render as a node-link diagram); - the\n**insights** grouped by explainer, filterable by confidence band, so you can see what each finding is and how certain it is; **extraction quality**— per-service coverage, unresolved routes, and samples of skipped files and parse errors, which is where you look when a snapshot seems thin.\n\nIt is strictly a viewer: every request reads through the same concurrency-safe accessors the MCP tools use and never mutates server state. It binds loopback only and serves nothing but that one page. Pass `--no-dashboard`\n\nto skip it.\n\nAgent tooling starts one enola server per session, so opening four terminals means four servers — each with its own graph, its own dashboard, and its own ephemeral port. Two things keep that legible.\n\n**One bookmarkable URL.** Besides its own port, every server competes for a fixed **shared URL**, `http://127.0.0.1:7171`\n\nby default. The first to start wins it; when that one exits another takes over within a few seconds, so the address keeps working for as long as any server is up. Whichever server answers there lists all the others. Set `ENOLA_DASHBOARD_PORT`\n\n(or `dashboard.port`\n\nin the config) to move it, or `ENOLA_DASHBOARD_PORT=off`\n\nto keep only the ephemeral ports.\n\n**Every page describes its own server.** The PID, uptime, repos and per-server call counts on a page belong to the process serving it — never to whichever server happened to start last. If a page shows a graph you did not expect, the switcher tells you which server holds the one you want.\n\nRunning servers register themselves under `~/.enola/instances/`\n\n; a record is removed on exit, and one left behind by a hard-killed process is cleaned up by the next reader. Each workspace also keeps its own graph receipt under `~/.enola/graphs/`\n\n, so restarting a server in one repo restores *that* repo's graph rather than whatever another terminal snapshotted last.\n\n— the concept, the fact model, the pipeline, and the full tool reference.[ARCHITECTURE.md](/enola-labs/enola/blob/main/ARCHITECTURE.md)— ready-made per-language and multi-repo configs.[examples/](/enola-labs/enola/blob/main/examples)\n\nApache License 2.0 — see [ LICENSE](/enola-labs/enola/blob/main/LICENSE).\n\nenola bundles third-party components under their own licenses; see [ NOTICE](/enola-labs/enola/blob/main/NOTICE). Swift parsing uses the\n\n[tree-sitter-swift](https://github.com/alex-pinkus/tree-sitter-swift)grammar by Alex Pinkus (MIT), vendored under\n\n[.](/enola-labs/enola/blob/main/internal/extractors/swiftextractor/grammar)\n\n`internal/extractors/swiftextractor/grammar/`", "url": "https://wpnews.pro/news/enola-mcp-architectural-snapshot-server-and-knowledge-graph", "canonical_source": "https://github.com/enola-labs/enola", "published_at": "2026-07-25 20:46:39+00:00", "updated_at": "2026-07-25 20:52:17.034535+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "ai-agents"], "entities": ["Enola Labs", "enola", "Model Context Protocol", "Claude Code", "Cursor", "GitHub Copilot"], "alternates": {"html": "https://wpnews.pro/news/enola-mcp-architectural-snapshot-server-and-knowledge-graph", "markdown": "https://wpnews.pro/news/enola-mcp-architectural-snapshot-server-and-knowledge-graph.md", "text": "https://wpnews.pro/news/enola-mcp-architectural-snapshot-server-and-knowledge-graph.txt", "jsonld": "https://wpnews.pro/news/enola-mcp-architectural-snapshot-server-and-knowledge-graph.jsonld"}}