axon is a local Rust architecture intelligence server for AI coding agents. Instead of letting agents guess about your codebase structure, axon extracts Rust facts from source code, stores them as machine-checkable relations in an embedded Datalog engine (CozoDB), and answers architectural questions with proof-carrying results.
It focuses on one thing: the implemented architecture. The primary graph is Rust-native: workspace, crate, module, source file, symbol, imports, and calls. Higher-level language such as DDD or design patterns is represented as an optional semantic overlay with evidence, not as the ground truth. Cozo Validity snapshots preserve recent history for temporal diffs.
MCP is an adapter, not the engine. The live model is maintained by the local process and can be queried through MCP, CLI commands, or the built-in web graph UI.
- Rust AST extraction — Rust source indexing via
syn, focused on a 1:1 Rust structure graph - Datalog reasoning — Transitive dependencies, cycle detection, layer violations, blast radius, dead code analysis
- Semantic overlays — DDD and pattern candidates can annotate Rust nodes without replacing Rust facts
- Impact analysis — Compute the blast radius of any change before making it
- Safe deletion — Proof-backed answers to "can I delete this?" with witness references
- Live file watching — Background watcher keeps the implemented model in sync as you code
- Temporal history — Recent implemented-graph snapshots show what changed over time
- Web graph UI — Human-scale overview of crates, modules, submodules, structs, and weighted architecture edges; complete Rust facts remain available to MCP
- Multi-crate workspaces — Isolated in-memory stores with workspace member support
This tap tracks main: brew install flavioaiello/axon/axon checks out the
current main branch and builds it locally.
brew tap flavioaiello/axon https://github.com/flavioaiello/axon
brew install flavioaiello/axon/axon
brew services start flavioaiello/axon/axonTo pull a newer commit after it lands on main:
brew update
brew reinstall flavioaiello/axon/axon
brew services restart flavioaiello/axon/axonaxon --version reports the main build plus the embedded commit id.
The MCP serve command bridges to the shared daemon. If the daemon is not
running, serve exits with an error instead of starting a separate in-process
model.
git clone https://github.com/flavioaiello/axon.git
cd axon
cargo install --path .When developing axon itself, remember that MCP clients run the configured
binary, not the source tree. Use cargo install --path . to update the axon
on your PATH, or point your local MCP config at target/debug/axon after
running cargo build.
axon exposes the same live model through multiple adapters:
| Adapter | Use case |
|---|---|
| MCP stdio | GitHub Copilot, Claude Code, Codex, and other MCP-capable agents |
| CLI | Universal fallback for Claude Code, Codex, scripts, and terminals |
| Local web UI/API | Human inspection and extension integrations that can call HTTP |
Different agents use different MCP configuration files. The server command is the same, but the file location and top-level schema are not interchangeable.
| Client | VS Code / VSCodium use | Project-shared config | Personal config | Top-level key |
|---|---|---|---|---|
| GitHub Copilot Chat in VS Code | Native VS Code MCP support | .vscode/mcp.json |
VS Code user mcp.json |
servers |
| GitHub Copilot Chat in VSCodium | Only if your installed agent extension supports VS Code-style MCP | .vscode/mcp.json |
Extension/user profile config | servers |
| GitHub Copilot CLI | Terminal inside either editor | none | ~/.copilot/mcp-config.json |
mcpServers |
| GitHub Copilot cloud agent / code review | GitHub.com, not the editor | Repository Settings > Copilot > MCP servers | none | mcpServers |
| Claude Code | Terminal or extension inside either editor | .mcp.json |
~/.claude.json |
mcpServers |
| Codex CLI / Codex IDE extension | Terminal or extension inside either editor | .codex/config.toml |
~/.codex/config.toml |
mcp_servers |
Use one entry per client family. VS Code does not read .mcp.json, Claude Code
does not read .vscode/mcp.json, and Codex does not read either JSON format.
The examples below assume the Homebrew install above and the shared daemon is
running. On Apple Silicon, the Homebrew binary is usually
/opt/homebrew/bin/axon; on Intel Macs, use /usr/local/bin/axon. If the agent
is launched from a shell where axon is already on PATH, command = "axon"
or "command": "axon" is also fine.
Add to .vscode/mcp.json in your project:
{
"servers": {
"axon": {
"type": "stdio",
"command": "/opt/homebrew/bin/axon",
"args": ["serve", "--workspace", "${workspaceFolder}"]
}
}
}Once the Homebrew service is running and this is configured, GitHub Copilot Chat in VS Code can discover axon tools, resources, and prompts automatically. VSCodium follows the same file shape only when the installed agent extension implements VS Code-style MCP support.
For local axon development, build first and point VS Code at the workspace binary so MCP requests exercise your edited source:
{
"servers": {
"axon-dev": {
"type": "stdio",
"command": "${workspaceFolder}/target/debug/axon",
"args": ["serve", "--workspace", "${workspaceFolder}"]
}
}
}The default serve command bridges to the shared daemon. Restarting the
Homebrew service restarts that daemon, but it does not rebuild or reinstall a
locally edited binary.
GitHub Copilot CLI uses its own user-level MCP file, not .vscode/mcp.json.
Add axon to ~/.copilot/mcp-config.json:
{
"mcpServers": {
"axon": {
"type": "stdio",
"command": "/opt/homebrew/bin/axon",
"args": ["serve", "--workspace", "/absolute/path/to/rust/workspace"],
"tools": ["*"]
}
}
}For GitHub Copilot cloud agent or Copilot code review on GitHub.com, add the
same kind of mcpServers entry in the repository's Copilot MCP settings instead
of committing a file. Adapt the command and args for the cloud runner: a
macOS Homebrew path such as /opt/homebrew/bin/axon is local-machine specific,
so install axon in the agent setup workflow or use a command path already
available in that environment.
Claude Code can use a project-shared .mcp.json file:
{
"mcpServers": {
"axon": {
"type": "stdio",
"command": "/opt/homebrew/bin/axon",
"args": ["serve", "--workspace", "${CLAUDE_PROJECT_DIR:-.}"]
}
}
}You can also register the same server from a VS Code or VSCodium terminal:
claude mcp add --scope project --transport stdio axon -- \
/opt/homebrew/bin/axon serve --workspace '${CLAUDE_PROJECT_DIR:-.}'For a private current-project config, omit --scope project; Claude Code stores
that local-scoped entry in ~/.claude.json. For a private all-projects config,
use --scope user.
If a GUI-launched Claude Code extension does not inherit your shell PATH, keep
the full Homebrew binary path:
claude mcp add axon -- /opt/homebrew/bin/axon serve --workspace /path/to/rust/workspaceClaude Code extensions that expose MCP settings can use the same command and
args as the .mcp.json example above.
Codex stores MCP servers in TOML. For a project-shared setup, add
.codex/config.toml:
[mcp_servers.axon]
command = "/opt/homebrew/bin/axon"
cwd = ".."
args = ["serve", "--workspace", "."]The project config lives in .codex/, so cwd = ".." starts axon from the
repository root before resolving --workspace ..
For a private user-level setup shared by Codex CLI and the Codex IDE extension,
put the same table in ~/.codex/config.toml with an absolute workspace path:
[mcp_servers.axon]
command = "/opt/homebrew/bin/axon"
args = ["serve", "--workspace", "/absolute/path/to/rust/workspace"]Or add it with the Codex CLI:
codex mcp add axon -- /opt/homebrew/bin/axon serve --workspace "$PWD"Codex project config is loaded only after Codex trusts the project. The Codex
CLI and Codex IDE extension share the same ~/.codex/config.toml and
.codex/config.toml layers, whether they are launched from VS Code, VSCodium,
or a standalone terminal.
The canonical tool names are Rust-native and actual-state first. Older names such as architecture, query_blast_radius, scan_model, and set_model are neither advertised nor accepted as tool names; use the rust_* names below.
| Tool | Description |
|---|---|
rust_status |
Current actual-state Rust model: crates, modules, source files, symbols, imports, calls, semantic annotations, health, and snapshot freshness |
rust_graph |
Bounded graph-database views over Rust modules, source files, symbols, imports, references, calls, AST edges, neighborhoods, paths, and relation counts; repeated facts are returned as compact schema + cols + rows JSON with offset/next_offset pagination and machine-readable exhaustiveness metadata |
rust_readiness |
Product-readiness report for agents: graph confidence, semantic call-resolution coverage, rust-analyzer availability, cargo metadata visibility, version/runtime identity, and remediation actions |
rust_impact |
Blast-radius and shape analysis over modules, structs, symbols, dependencies, fields, methods, call graph reachability, optimization/refactor recommendations, and Rust practice findings |
rust_delete_safety |
Proof-backed safe-deletion check for structs/symbols with inbound call/import/AST witnesses; module is optional |
rust_invariants |
Evaluate actual graph invariants and configured constraints: layer violations, cycles, aggregate quality, orphans, policy violations, drift freshness |
rust_explain |
Evidence-backed explanation with witness paths for failing invariants or constraints |
rust_history |
List actual Rust graph snapshots, compare two snapshot timestamps, or compare the two most recent snapshots with mode: "latest_diff" |
rust_search |
Search Rust facts and semantic annotations by keyword |
| Tool | Description |
|---|---|
rust_scan |
Unified scan of workspace source code: refreshes the actual Rust fact graph from AST facts, code references, and compiler-resolved calls when rust-analyzer succeeds; use rust_graph to inspect persisted facts |
rust_annotations |
Create, update, or remove semantic annotations on top of Rust facts with compact kind/name/module plus data arguments; does not mutate source-extracted ground truth |
rust_diagnose |
Diagnose and plan from actual Rust facts; accept/reset are compatibility no-ops in actual-first mode |
rust_constraints |
Declare and evaluate constraints: layer assignments, allowed/forbidden dependencies |
| URI | Content |
|---|---|
axon://architecture/overview |
Rust ontology contract, semantic overlays, health inputs, and rules |
axon://rust/ontology |
Rust-native facts and overview projection guidance |
axon://architecture/rules |
Architectural constraints |
axon://architecture/conventions |
Naming, structure, and testing conventions |
axon://context/{name} |
Compatibility semantic-overlay details |
| Prompt | Description |
|---|---|
axon_guidelines |
Rust architecture workflow guidance enriched with live Datalog health, constraints, temporal drift, and semantic-overlay context |
axon [command] [options]
| Command | Description |
|---|---|
serve |
Start the MCP stdio bridge to the shared daemon; accepts --workspace <path> (defaults to current directory). Use --standalone for an in-process server; --web-port only applies in standalone mode |
daemon |
Run the shared in-memory daemon, Unix socket bridge, live watcher, and multi-workspace web graph; accepts --socket <path> and --web-port <port> |
web |
Start the background Rust watcher and local web graph for one workspace; accepts --workspace <path> (defaults to current directory) and --port <port> |
export <file> |
Export a workspace model to JSON; requires --workspace <path> and accepts --state actual or compatibility aliases |
list |
Show all discovered crates and their model status; accepts --workspace <path> (defaults to current directory) |
check |
Parse live imports and report imports that do not map to the current actual semantic overlays; requires --workspace <path> |
scan |
Run the unified Rust scan, save actual facts, compute temporal drift, and attempt rust-analyzer resolved-call enrichment; requires --workspace <path> |
The daemon socket defaults to $AXON_SOCKET when set, otherwise
~/.axon/daemon.sock.
Examples:
axon web --workspace .
axon serve --workspace .
axon serve --workspace . --standaloneOpen http://127.0.0.1:8888 to inspect the live Rust architecture overview. If the port is occupied, axon reports a bind error instead of switching ports.
┌──────────────┐ ┌──────────────────────────┐
│ Rust source │──────►│ live Rust AST indexer │
└──────────────┘ └────────────┬─────────────┘
│
┌──────────▼──────────┐
│ in-memory Cozo graph │
└──────────┬──────────┘
│
┌──────────────────┼──────────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
│ MCP │ │ CLI │ │ Web/API │
│ adapter │ │ adapter │ │ adapter │
└─────────┘ └─────────┘ └─────────┘
- Ingest — The Rust AST scanner extracts structural facts (types, imports, modules, calls) from source code
- Store — Facts are normalized into in-memory CozoDB relations with
Validityhistory for the running process - Reason — Datalog rules derive transitive dependencies, cycles, violations, and blast radius
- Expose — MCP and CLI can query complete facts; the web UI renders a compact overview projection
- Watch — Background Rust file watcher keeps the implemented model in sync (2-second debounce)
axon stores Rust structure first:
- Workspace — Repository root under analysis
- Crate — Cargo package boundary
- Module / submodule — Rust module tree derived from files and
moddeclarations - Source file — Concrete
.rsfile location - Symbol — Structs, enums, methods, and other Rust symbols discovered by the scanner
- Edges —
contains,declares,imports,calls, and related structural facts
DDD and design-pattern vocabulary lives as semantic labels on top of these Rust nodes. For example, a struct may carry an entity_candidate label, but it remains a Rust struct in the graph.
The web UI deliberately does not draw every fact. It shows the map a human needs for orientation:
- Visible nodes — crate, module, submodule, struct
- Visible edges — containment, declarations, aggregated imports, aggregated calls
- Hidden but stored facts — source files, methods, functions, enums, call sites, import records, AST edges
Mermaid is useful as an export format for a selected slice: a dependency path, a proposed refactor, or a PR/design note. It is not the best primary live UI for the whole graph because Rust call graphs become dense quickly and Mermaid's static layout has limited drill-down. The primary UI should stay interactive, filterable, and layered; Mermaid can be generated from the selected subgraph when a stable diagram is useful.
Sub-structures (fields, methods, parameters, invariants) are stored as independent CozoDB relations, not nested JSON. This enables cross-cutting Datalog queries that would be impossible with flat document storage.
Reasoning tools return structured results with:
- status — Tool-specific state such as
true/false,reachable,in_sync,pending_changes,ready, ornot_scanned - proof — Derivation rules and witness counts when the result comes from the reasoning kernel
- evidence — Supporting facts, paths, relation counts, and source locations when available
- limitations — Explicit uncertainty, such as dynamic dispatch, generated code, missing scans, or stale drift
- truth_maintenance — Snapshot and drift context for persisted reasoning claims
The system prefers bounded, evidence-backed answers. If the stored graph is missing or incomplete, tools surface that through status, limitations, and next actions instead of silently treating absence as proof.
{
"schema": "axon.rust_graph.relations.v1",
"format": "schema_rows",
"view": "relations",
"cols": ["rel", "count"],
"rows": [["symbol", 210], ["calls_symbol", 2952]],
"proof": { "rule": "bounded Rust graph query over persisted Cozo relations" }
}{
"status": "ok",
"health": {
"score": 85,
"circular_deps": [],
"module_cycles": [],
"layer_violations": [],
"missing_invariants": [["Catalog", "Category"]]
},
"graph_confidence": {
"status": "usable_with_warnings",
"score": 85,
"counts": { "source_files": 14, "symbols": 210, "resolved_call_edges": 198 }
},
"readiness_summary": { "status": "usable_with_warnings" },
"proof": { "rule": "architecture overview combines implemented graph reconstruction with health and temporal diff summary" }
}{
"status": "false",
"claim_kind": "safe_to_delete",
"context": "Billing",
"entity": "Order",
"can_delete": false,
"result": {
"events_sourced": ["OrderPlaced", "OrderCancelled"],
"repositories_managing": ["OrderRepository"],
"import_references": [],
"ast_references": [],
"call_references": [
{ "caller": "process_payment", "file": "src/billing/service.rs", "line": 42 }
]
},
"proof": { "rule": "entity deletable IFF no inbound references are present in the stored implemented graph" },
"limitations": ["Dynamic dispatch, reflection, string-based lookups, and out-of-repository consumers are not tracked."]
}{
"status": "latest_diff",
"claim_kind": "history",
"state": "actual",
"ts_old": 1760000000000000,
"ts_new": 1760000001000000,
"summary": { "total_changes": 3, "additions": 2, "removals": 1 },
"added": [
{ "kind": "context", "action": "add", "context": "", "name": "Notifications" },
{ "kind": "field", "action": "add", "context": "Catalog", "name": "sku", "owner_kind": "entity", "owner": "Product" }
],
"removed": [
{ "kind": "entity", "action": "remove", "context": "Ordering", "name": "LegacyOrder" }
],
"proof": { "rule": "latest_diff compares the two most recent stored temporal snapshots" }
}| Language | Parser | Coverage |
|---|---|---|
| Rust | syn crate |
Full AST parsing |
Non-Rust language support was intentionally removed so axon can focus on being excellent for Rust codebases.
This project is licensed under the MIT License.