Skip to content
Conduit

mcp server

eyrie

Meet Eyrie πŸ¦… Diagrams go stale the day you draw them. This one doesn't. Eyrie turns your codebase into a live architecture map. Save a file, or let an AI agent edit one, and the map updates right awa

eyrie-hq/eyriecommunitylistedunreviewed

Set up

Runs on your computer. Pick your agent and operating system.

This project does not publish an install method we can turn into a config. Follow the README instead.

README

synced Oct 22, 2026

Eyrie

CI License: AGPL-3.0-only

A live architecture map of your codebase that updates as you (or your coding agent) change the code.

Eyrie parses a repository locally, builds a code knowledge graph, derives a small architecture graph from it (pages β†’ API routes β†’ services β†’ data β†’ external services), and shows it as an interactive map. When a file is saved, only the affected part of the graph is recomputed and the change animates into the map: new components appear, edited ones pulse, and every save is recorded on a change tape at the bottom of the screen.

Status: early. Phase 1 (static architecture map) and Phase 2 (live graph) work end to end for TypeScript/JavaScript, Python, Go, Java, C#, PHP, Ruby and Rust projects. JavaScript (Next.js, Node.js) has the deepest support. See Roadmap.

An eyrie is a bird of prey's nest, built high on a crag β€” the vantage point you watch the whole territory from.

Quick start

Requirements: Node.js 22.13 or newer, pnpm 9.

pnpm install
pnpm start -- /path/to/your/repo

This builds the UI, indexes the repository, and opens http://127.0.0.1:4319/?token=… in your browser. Leave it running and edit code in the repository; the map follows.

Options: --port <n>, --no-open, --no-watch (index once), --reset (ignore the cache), --host <h>, --ai claude or --ai codex (notes on commits; see Commit history and AI notes).

Use it from a coding agent (MCP)

eyrie mcp serves a repository's live architecture to coding agents over the Model Context Protocol, on stdio. Any MCP client can run it; replace /path/to/eyrie with this checkout and /path/to/your/repo with the project. pnpm --silent keeps pnpm's own output off the protocol stream.

Claude Code and Codex have the same one-line setup:

claude mcp add eyrie -- pnpm --silent --dir /path/to/eyrie eyrie mcp /path/to/your/repo
codex mcp add eyrie -- pnpm --silent --dir /path/to/eyrie eyrie mcp /path/to/your/repo

Gemini CLI (.gemini/settings.json in the project, or ~/.gemini/settings.json) and Cursor (.cursor/mcp.json in the project, or ~/.cursor/mcp.json) read the same mcpServers block:

{
  "mcpServers": {
    "eyrie": {
      "command": "pnpm",
      "args": ["--silent", "--dir", "/path/to/eyrie", "eyrie", "mcp", "/path/to/your/repo"]
    }
  }
}

VS Code (GitHub Copilot in agent mode) reads .vscode/mcp.json, with servers instead of mcpServers:

{
  "servers": {
    "eyrie": {
      "type": "stdio",
      "command": "pnpm",
      "args": ["--silent", "--dir", "/path/to/eyrie", "eyrie", "mcp", "/path/to/your/repo"]
    }
  }
}

Tested end to end with Claude Code and Codex; Gemini CLI, Cursor and VS Code use their standard MCP configuration and were not tested here. Windsurf, Zed and other clients take the same command and arguments in their own MCP settings.

The agent then has read-only tools that answer from the same graph as the map, kept current as files are saved:

ToolAnswers
architecture_overviewComponents by lane, size, the most connected components
find_components, explain_componentWhat a component is, its files and symbols, what it uses and what uses it, recent commits that touched it
impact, traceEverything that depends on a component (what could break), or everything it reaches
find_pathThe chain of dependencies between two components
components_for_filesWhich components own the files the agent is about to edit
recent_changesWhat the agent's edits changed in the architecture since the server started
commit_historyRecorded commits, the agents that co-authored them, and AI notes when turned on

A name that matches several components is refused with the candidates rather than guessed. When the desktop app runs from a checkout, its project menu has Copy MCP Command for Claude Code and for Codex.

Commit history and AI notes

Whenever Eyrie opens or scans a project, it records the commits it has not seen yet in the project's knowledge.db: the message, the author and any coding agent in a Co-Authored-By trailer, the files changed with line counts, and the architecture components those files belong to. While a project is open it checks for new commits every 5 seconds.

Optionally, Eyrie has each commit noted by your Claude Code or Codex CLI: what the commit is for, whether what it changed matches its message, and its architectural risk. A commit titled "fix: typo in checkout" that also edits the data model is flagged. Turn it on per project in the desktop app (Analyse Commits with Claude or with Codex in the project's β‹― menu), or with --ai claude / --ai codex (or EYRIE_AI) on the command line. It is off by default. Codex is available on macOS only for now, because Eyrie runs it inside a macOS sandbox (see Privacy and security).

Notes are opinions, kept beside the facts and labelled with the model that wrote them. Every observation must cite a file or component the commit actually touched, or the note is rejected. A note never becomes a dependency, a finding or a rule.

Supported languages

LanguageFilesImports resolved through
TypeScript / JavaScript.ts .tsx .js .jsx .mjs .cjsrelative paths, tsconfig / jsconfig paths, workspace packages, barrels
Python.pydotted modules, packages and __init__.py re-exports, relative imports
Go.gogo.mod module paths; a package is its whole directory
Java.javapackages, single-type, wildcard and static imports
C#.csnamespaces, using directives and aliases
PHP.phpnamespaces, use (including groups), require / include
Ruby.rb .rakerequire / require_relative, autoloaded constants (Rails)
Rust.rsmod files, use trees, crate:: / self:: / super:: paths

Every language gets the same treatment: classes and functions, calls between them (including through fields such as self.repo, $this->repo, @repo and injected services), HTTP routes, database models with reads and writes, external SDKs and outbound HTTP calls. Mixed repositories (a Python or Go backend with a React frontend) are indexed into one map. See What is detected for the frameworks each language understands. JavaScript (Next.js, Node.js) has the deepest support.

Generated code is skipped (*.pb.go, *_pb2.py, *.Designer.cs, *.d.ts), and test files in each language's convention (test_*.py, *_test.go, *Test.java, *_spec.rb, …) are kept out of the architecture view.

What you see

  • Four lenses, from the whole system down to one request: 1 System, 2 Architecture, 3 Data flow, 4 Sequence. Eyrie opens on System the first time and remembers your choice after that. System is the whiteboard view: components are grouped into one box per app or package (folders under apps/, packages/, services/ or libs/) and per layer (UI, API, services, data), with each external service and any missing route kept as its own box, and links between boxes labelled with what they carry and how many component links they stand for. Select a box to see what it holds; click a component there to open it in the Architecture lens. Architecture is the map of every component the system is made of. Data flow keeps only the components that touch data β€” stores, and anything that reads, writes or ships it β€” and colours every link by what it carries: orange writes, cyan reads, dashed for a call that leaves the process, faint for an in-process hand-off. Reads are drawn pointing out of the store, so the arrows follow the data rather than the dependency. Sequence picks one entry point and lays its calls out as a sequence diagram: lifelines across the top, messages down the page, each with the file and line that makes the call (click it to open your editor). All four are derived from the same static analysis; see Known limitations for what sequence order can and cannot tell you.
  • 2D and 3D views (press V). Both use custom React Flow components for navigation and inspection. The 3D view adds a full-canvas Three.js city: components become lit blocks whose height shows lines of code, dependencies run along the ground, and upright labels remain readable as the camera rotates. Drag to pan, right-drag or βŒ₯-drag to orbit, scroll or pinch to zoom, Q/E to rotate, T for a top-down plan, R to reset. The minimap navigates the projected city. If WebGL is unavailable, the view offers a switch back to 2D.
  • Lanes. Components are arranged left to right in the order a request travels: UI β†’ Edge (middleware) β†’ API β†’ Logic β†’ Data β†’ External. A ruler at the top names the lanes and points to lanes that are off-screen.
  • Colors carry meaning. Blue frontend, green API, orange services, purple AI, cyan data, yellow external services, grey utilities. A missing API route (frontend calls /api/x and nothing serves it) is drawn in red.
  • Links. Solid lines are calls, dashed lines are HTTP requests (they cross a network boundary), cyan lines read or write data.
  • Live changes. New components get a green + badge, edited ones a yellow ~, removed ones a red βˆ’. In 3D, new blocks rise into place and removed blocks briefly fade and sink. Selecting a component sends a bounded sweep along its dependencies; trace modes animate continuous request flow. The 2D lane ruler marks off-screen changes. Layout is stable: existing components keep their order when the map updates.
  • Change tape. Every save becomes a tick: height is the size of the change, color its type. Hover a tick to light up what it touched; click to jump there.
  • Details panel. Click a component to see its purpose (from its doc comment, or generated from the code), source location with a code preview, Open in editor, what it calls, what uses it, and the symbols inside.
  • Trace and impact. In the panel, Depends on follows the request through everything a component calls (with animated flow); Impact shows everything that could break if it changes, grouped into API, UI, services, data and external.
  • Search (⌘K or /), category filters, F to fit, Esc to clear.

How it works

files ──► parser (tree-sitter) ──► facts per file ──► code graph ──► architecture graph ──┬─► UI (ELK layout; React Flow 2D,
             cached by content hash      (Graphology + SQLite)    (deterministic rules)     β”‚       Three.js city; anime.js motion)
                                              β–²                                            β”œβ”€β–Ί MCP server ──► coding agents
file watcher ── changed files only β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   diffs ──► WebSocket events ──► animations β”‚
git log ── new commits ──► knowledge.db ◄── components each commit touched β—„β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β–²
                              └── optional AI notes (your Claude Code CLI, opt-in)

Code graph first. Visualization second. AI third. Every relationship comes from static analysis. AI is optional and only annotates: today, notes on recorded commits. It never invents a dependency.

  1. Facts. Each file is parsed once with tree-sitter (WASM) into facts: imports, exports, symbols, call/new/JSX references with their enclosing symbol, literal arguments (for fetch('/api/...')), route registrations, ORM models. Every language has its own adapter, but they all produce the same facts, so everything downstream is language-neutral. Facts depend only on the file's content, so they are cached in SQLite by hash; restarts reuse them.
  2. Code graph. Facts become nodes (files, functions, classes, methods, components, hooks, API routes, middleware, data models, external services) and edges (CONTAINS, IMPORTS, EXPORTS, CALLS, USES, EXTENDS, IMPLEMENTS, HTTP_CALL, READS_FROM, WRITES_TO, DEPENDS_ON). Resolution follows relative imports, tsconfig/jsconfig paths and baseUrl, workspace packages, barrels and re-exports, class instances, this.field types, typed parameters and object-literal services. Outside JavaScript it also follows Python dotted modules and packages, Go packages (directories, via go.mod), Java / C# / PHP namespaces and wildcard imports, Rust mod trees and use paths, and Ruby autoloaded constants, plus names visible without an import (same Go package, same Java or C# namespace). self, receivers, $this, @ivar and injected fields all resolve like this.field. Every edge carries a confidence.
  3. Lenses. Data flow and sequence are pure functions of the architecture graph, computed in the browser, so they stay live as you edit. Data flow keeps a component when it stores data, reads, writes or ships it, or when it sits on a path from an entry point to one that does; each edge is classified read, write, readwrite, transfer (it crosses a process boundary) or carry. Sequence walks the graph from one entry point, ordering each component's calls by the source line the graph recorded for them, and expanding a component's own calls only the first time it is reached. Its canvas uses custom React Flow lifeline nodes and message edges, with pan, zoom, fit/reset controls, and a pinned source column that follows the viewport vertically.
  4. Incremental updates. While resolving a file, the graph records which other files it consulted. When a file changes, only it is re-resolved, plus, if its exported surface changed, the files that consulted it. Node ids are stable hashes of repository + file path + symbol kind + qualified name, so unchanged code keeps its identity across edits. Each update produces an exact diff (node.created, edge.deleted, …).
  5. Architecture graph. Files are classified by framework conventions and names (Next.js page/route/layout/middleware, 'use server', Express routers, *Service, *Repository, services/, React components, hooks). Presentational components, hooks, helpers and SDK client wrappers are hidden and edges pass through them, so the map stays small and connected. Shared helpers used by many components, and modules on dependency paths in library-style repos, are shown. A typical app reduces to 20–50 components.

What is detected

AreaDetected
Next.jsApp Router page, route (per HTTP method), layout, middleware/proxy, Pages Router pages and pages/api, next/dynamic imports, server actions
HTTPfetch, axios and axios.create() instances, ky, ofetch, useSWR, template and concatenated URLs, matched to routes including dynamic segments; external hosts mapped to providers
Node.jsExpress/Hono/Fastify-style app.get('/path', handler) routes, CommonJS require/module.exports
DataPrisma schema models with reads/writes per operation, Drizzle tables, Mongoose models, Supabase tables
PythonFlask / FastAPI decorator routes (with blueprint and router prefixes), Django urlpatterns; Django, SQLAlchemy and SQLModel models; requests / httpx calls
Gonet/http (including Go 1.22 "POST /x" patterns), gin, echo, chi, fiber and gorilla/mux routes; gorm models; http.Get calls
JavaSpring MVC (@RequestMapping + @GetMapping…), JAX-RS and Micronaut routes; JPA entities and Spring Data repositories
C#ASP.NET controllers ([Route], [HttpGet], [controller] tokens) and minimal APIs (MapGet, MapGroup); EF Core DbSet models; HttpClient calls
PHPLaravel routes (groups, prefixes, match, resource) and Symfony #[Route]; Eloquent models and relations
RubyRails routes.rb (resources, namespace, to:) and Sinatra; ActiveRecord models and associations
Rustaxum .route(), actix-web / rocket attribute routes; diesel, sea-orm and sqlx models
External services50+ SDKs (OpenAI, Anthropic, Gemini, Stripe, Resend, Clerk, Supabase, Redis, S3, …) via imports or API hosts, recognised under each ecosystem's package names (pip, Go modules, Maven, NuGet, Composer, gems, crates)
Project setuptsconfig/jsconfig paths (including extends), monorepo workspace packages, go.mod modules, frameworks from pyproject.toml / requirements.txt, pom.xml / build.gradle, *.csproj, composer.json, Gemfile and Cargo.toml, .gitignore and .eyrieignore

Privacy and security

Eyrie has no accounts, no telemetry and no server of its own. It stores two files per repository under ~/.eyrie/repos/<hash>/: an analysis cache (graph.db: file paths, symbol names and the graph built from them) and knowledge.db, the commits it has recorded (messages, authors' and co-authors' names and email addresses as git has them, changed paths) and any AI notes on them. Delete either at any time.

  • With AI notes off, the default, nothing leaves your machine. Eyrie makes no network requests; fonts are bundled.
  • With AI notes on for a project, Eyrie runs your Claude Code or Codex CLI on recorded commits. It sends commit messages, author names, changed file paths with line counts and the names of the architecture components involved, and never source code. The CLI sends them to Anthropic or OpenAI under your own account.
    • Claude Code runs with no tools, no MCP servers and none of your settings, in an empty temporary folder, so the model can read nothing else and change nothing.
    • Codex keeps a command tool that cannot be turned off, and its own read-only sandbox still lets commands read any of your files. So Eyrie runs Codex itself inside a macOS sandbox: it can write nowhere except one empty workspace (holding a copy of Codex's login) and /dev, and it cannot read file contents or list folders in your home folder, your temporary folder or on external drives. Codex's command sandbox cannot start inside it, so no command runs at all, and Codex also runs read-only, without your config, rules, saved session, web search, connectors, plugins or its code-mode tools. Tested with Codex's own protections deliberately switched off: its patch tool and shell could write only inside the workspace, and reading ~/.ssh was refused. System files outside your home folder, such as /etc/hosts, stay readable, and no command can run to read them.
  • The server binds to 127.0.0.1, requires a random per-run access token, and rejects requests whose Host header is not local (DNS-rebinding protection).
  • The source endpoint only serves files that are in the index. .env files, keys and anything ignored by git are never readable through it.
  • The MCP server has no network listener: it talks only to the agent that started it, over stdio, and every tool is read-only.
  • git runs without a shell and with the settings a repository could use to run its own programs turned off.
  • The data lives in ~/.eyrie/repos/<hash>/ (override with EYRIE_HOME). Delete that directory, or run with --reset, to start fresh.

Found a security problem? Please report it privately. See SECURITY.md.

Development

packages/
  core/          shared types: facts, graph, stable ids, diffs, events
  parser/        tree-sitter (WASM) language adapters β†’ facts (TS/JS, Python, Go, Java, C#, PHP, Ruby, Rust); Prisma schema reader
  graph/         module resolution, relationship resolution, incremental code graph, queries
  architecture/  code graph β†’ architecture graph (classification, lifting, pass-through)
  lens/          views over the architecture graph: data flow, sequence, System; trace, impact and paths
  storage/       SQLite (node:sqlite) facts cache, nodes and edges
  engine/        scanning, ignore rules, config loading, watching, update pipeline
  knowledge/     knowledge.db: recorded git commits mapped onto components; optional AI notes (Claude Code or Codex CLI)
  server/        CLI, HTTP API, WebSocket and the MCP server (`eyrie mcp`)
apps/
  web/           React UI: ELK layout, React Flow navigation, Three.js 3D city, anime.js animation
  desktop/       Electron shell (Electron Forge): runs the engine in a utility process and shows the UI
fixtures/
  bookstore-app/ sample Next.js app used by tests and demos
scripts/
  fetch-smoke-repo.sh  clones a real OSS app into .smoke/ for the smoke test
LICENSE          GNU AGPL-3.0
THIRD-PARTY-NOTICES.md  licences of bundled dependencies and fonts
pnpm test          # parser, graph, architecture, lens, engine (real file watcher) and server tests
pnpm typecheck
pnpm dev           # engine on :4319 (fixture app, token "dev") + UI with hot reload on :5173
pnpm desktop       # the desktop app (Electron); see apps/desktop/README.md
pnpm eyrie mcp fixtures/bookstore-app   # the MCP server on stdio, for trying it with a client

pnpm smoke:fetch   # clone a real MIT-licensed Next.js app into .smoke/ (once)
pnpm smoke         # scan that real repo end to end; skips if it was never fetched

pnpm test is hermetic and offline: it runs against fixtures/bookstore-app, a small hand-written Next.js app whose every asserted edge is a claim about how resolution should behave. pnpm smoke is the opposite check β€” it scans a ~290-file third-party repo to confirm Eyrie survives code nobody wrote for it, asserting shape and health rather than specific edges. That repo is cloned into the gitignored .smoke/, never vendored, so this project carries no third-party code.

The other languages are tested the same way, with small in-memory apps per language (packages/*/test/languages.test.ts). They were also checked by hand against real open-source apps: the FastAPI full-stack template, Spring petclinic, and the Go, C#, Laravel, Rails and Rust RealWorld implementations.

Adding a language. Add its tree-sitter grammar to packages/parser, register the extension in languages.ts, and write an extractor on top of BaseExtractor (packages/parser/src/base.ts) that emits the same facts as the others. Then teach ModuleResolver (packages/graph/src/resolver.ts) how its imports map to files. Nothing else needs to know about the language.

During pnpm dev, open http://localhost:5173/?token=dev.

To open files from the UI, Eyrie looks for cursor, code, windsurf or zed on your PATH; set EYRIE_EDITOR to choose one.

Roadmap

  • Phase 1: static architecture map. Scanner, tree-sitter parsing, symbols, imports/exports, calls, React and Next.js detection, code graph, architecture graph, interactive map, details panel.
  • Phase 2: live graph. File watcher, incremental resolution with reverse-dependency invalidation, graph diffs, WebSocket events, animated updates, stable layout, change tape.
  • Phase 3: navigation. Done: search, open in editor, jump to line, category filters, data-flow, sequence and System lenses. Next: nested group boundaries, expanding a box in place.
  • Phase 4: graph intelligence. Done: trace (downstream) and impact (upstream) on the architecture graph, and the shortest path between two components (MCP find_path). Next: path finding in the UI, code-level impact.
  • Phase 5: AI understanding. Done: commit history in knowledge.db and optional AI notes on each commit through the Claude Code or Codex CLI. Next: architecture rules and findings with AI explanations (Knowledge Loop), more backends.
  • Phase 6: Git intelligence. Working-tree overlays, branch and commit architecture diffs, timeline.
  • Phase 7: coding-agent integration. Done: the MCP server (eyrie mcp) and agent attribution from Co-Authored-By trailers. Next: an MCP check tool and Claude Code hooks that hand new architecture violations back to the agent, and the MCP server in the packaged desktop app.
  • Desktop app. An Electron shell (Electron Forge) around the same UI, running the engine in a utility process. A first, unsigned version packages and runs on macOS from apps/desktop; signing, installers for every platform and background watching are next.

Known limitations

  • Resolution is syntactic. Without a type checker, calls through untyped values, dependency injection or dynamic dispatch can be missed or fall back to the class (with lower confidence).
  • The sequence view orders calls by where they are written in the source. That is all static analysis can give: conditionals, loops and await can make the running order differ, and returns are not modelled. Workflow and lifecycle views (branches, retries, terminal states) would need control-flow analysis, which Eyrie does not do.
  • Architecture classification is heuristic. It is tuned most for Next.js and Node.js apps, then for the frameworks listed above; other layouts may need rules. Add a doc comment (or a Python docstring) to a component's main export to set its description.
  • Express router prefixes (app.use('/api', router)) are not joined to the routes registered on the router yet.
  • Parsing is single-threaded; very large repositories take a while on first run (later runs use the cache).
  • Supported languages: TypeScript, JavaScript, Python, Go, Java, C#, PHP, Ruby and Rust. Kotlin, Swift and C/C++ are not parsed yet.
  • Outside JavaScript, what a dynamic language decides at run time is not followed: Ruby metaprogramming, PHP magic methods, Python getattr, DI containers configured outside the code, and Rust macro bodies (such as sqlx::query!).
  • A frontend that calls the backend through a generated API client (OpenAPI SDKs) shows no HTTP links to the backend's routes; only direct fetch / axios calls are matched.

Related projects

This space moves fast. GitNexus, Understand-Anything, Depwire and CodeViz build code graphs for agents and humans. Eyrie focuses on a small, deterministic architecture view that stays live while code changes.

Contributing and support

Eyrie is maintained by Badhan Sen (@Badhansen).

License

GNU AGPL-3.0-only. You can read, run, modify and redistribute Eyrie freely. The one condition that matters: if you modify Eyrie and let other people use it over a network, you have to offer them your modified source too. Running it locally on your own code carries no obligation at all, and Eyrie never sends your code anywhere.

Eyrie depends on open-source packages and bundles two fonts under their own licences; see THIRD-PARTY-NOTICES.md.

↑↓ navigate openesc close