Analyzes Python import graphs to detect architectural violations, dependency cycles, and the blast radius of code changes.
Also known as hslee16/archy
- Stars
- 8
- Tools
- —
- Downloads, 30 days
- 6,756
- Latest version
- 0.55.3
- Last commit
- Nov 13, 2026
- License
- MIT
What is archy MCP?
archy analyzes Python import graphs to detect architectural violations, dependency cycles, and the blast radius of code changes.
Summary generated from the project's README
About archy MCP server
Analyzing Python import graphs to detect architectural violations and dependency cycles is what archy enables for developers. It transforms a project's import structure into actionable data, allowing an AI agent to understand the blast radius of a code change or identify which tests are affected by a specific edit. By parsing imports and function calls, it provides insights into module risk and structural integrity that a standard file tree cannot reveal.
Users can integrate this tool into their workflow as a CLI for continuous integration or as an MCP server within an agent session. It relies on a configuration file to define declared layers and forbidden directions, failing when the actual import graph disagrees with these rules. The server provides specific capabilities such as detecting cycles, checking layer contracts, and scoring modularity, depth, and complexity to ensure the codebase maintains its intended design.
What you can do with it
- 1Detect architectural layer violationsAn AI agent can check if a new import violates the predefined directional rules of your project layers.
- 2Assess code change impactBefore applying an edit, an agent can determine the blast radius and which tests will be affected by the change.
- 3Monitor structural health scoresYou can track modularity, acyclicity, and complexity metrics to see how a commit affects the overall architecture.
Written from the project's README. Every use case is checked against a quote from it.
How to install archy MCP
Formats verified 10 Nov 2026Install archy MCP in Claude Code
$ claude mcp add archy -- uvx archyInstall archy MCP in Cursor
~/.cursor/mcp.json
{
"mcpServers": {
"archy": {
"command": "uvx",
"args": [
"archy"
]
}
}
}Install archy MCP in VS Code
.vscode/mcp.json
{
"servers": {
"archy": {
"type": "stdio",
"command": "uvx",
"args": [
"archy"
]
}
}
}Install archy MCP in Claude Desktop
claude_desktop_config.json
{
"mcpServers": {
"archy": {
"command": "uvx",
"args": [
"archy"
]
}
}
}Install archy MCP in Codex
~/.codex/config.toml
[mcp_servers.archy] command = "uvx" args = ["archy"]
Install archy MCP in Gemini CLI
~/.gemini/settings.json
{
"mcpServers": {
"archy": {
"command": "uvx",
"args": [
"archy"
]
}
}
}Automated checks
Ran Nov 12, 2026Full report →Passed our automated checks. That is not a security audit: read the code and give the server the narrowest access it needs. How checks work
- Source repository linked and reachablePass
- Open source license (OSI)Pass
- Commit or release in the last yearPass
- Package name not close to a popular packagePass
- Remote endpoint uses HTTPSNot checked
- Destructive tools are labelledNot checked
- No hidden instructions to the modelPass
- No critical advisory in the package or its dependenciesPass
README
View on GitHubYour folders show your architecture. Your imports decide it. archy turns a Python import graph into something an agent can use: blast radius before an edit, the tests that edit affects, the modules most at risk. And it fails when the graph disagrees with the layers you declared. Same graph either way, as a CLI and an MCP server, every session and in CI.
[!IMPORTANT] Status, 2026-09-10: active, and pointed at the loop rather than the surface area.
What archy does. One graph, two front ends. It parses a Python project's imports and calls once, keeps the parse cached, and answers the questions a file tree cannot: what does this change reach, which tests does that affect, where are the cycles, and does the graph still agree with the layers you declared. It runs as a CLI in CI and as an MCP server inside an agent session, off the same graph either way, and it fails when the declared architecture and the real one disagree.
Where it is going: deeper into that loop, not wider. The local-model question that reopened this project on 2026-09-02 is closed, as six interventions, none positive, two of them measured as costing MORE than their control. The write-up is Study 8 in
docs/WHAT_DIDNT_WORK.md, and it is the fifth pre-registered null published here. The feature that line produced,conventions --emit-headers, is kept, because it is derived rather than hand-authored, cheap, and--checkstops it rotting. Only the claim attached to it is retired.What those six nulls did establish is where the failure lives: delivery and retrieval, not capability. Across 132 agent transcripts on a pinned tree,
readfired in 132 of 132 runs with a median of 9.5 calls before the first edit; archy fired in 84 of 132, with a median of zero calls before it. Every archy surface is pull. The model reaches for it after it has already decided, or not at all. Two payloads pushed through the one channel archy owns, the file itself, both came out more expensive than their control, which is evidence about the channel and not the content.So the working design rule is: put the answer in the default output of a command that is already being run, rather than adding a surface that has to be discovered first. The live threads follow from it. One is the vacuity family: a check that could not have failed must not report like one that was evaluated and held, which is now explicit on the contracts result and is being finished across the remaining surfaces. The other is two structural refactors of archy's own code: the module its own tools rate as central and fragile, and the two files that have grown large enough to hide things. New tools and new output formats stay gated behind a usage signal.
The discipline does not relax because the project is active. Thresholds are pre-registered before a run, and any result, including another null, gets published in
docs/WHAT_DIDNT_WORK.mdlike the five before it. Nothing archy ships today claims a local-model benefit;archy brief(v0.46) shipped explicitly on judgment ahead of that measurement rather than on one, and the measurement did not later rescue it.What is not changing. The original use case is still supported and still works: layer governance in CI, blast radius and affected-tests for a frontier agent, the MCP server. Nothing is being removed or renamed. Bugs still get fixed, pull requests still get reviewed, and the
good first issuetickets are still deliberately left open.
Read this first: I measured the premise, and it was wrong
I built archy after watching coding agents produce changes that passed review and rotted the import graph underneath. Then I measured whether that happens, and it barely does.
| measurement | subject | rate |
|---|---|---|
| 25 live agent runs on the riskiest SWE-bench tasks | cycles or declared-layer violations | 0% (95% upper bound 12%) |
| 1,072 human commits, 11 repos | cycles introduced | 0.5% per commit |
| 151 commit pairs in projects that declare an architecture | contract violations | 0.66% per commit |
| 107 samples of those same projects over time | rules going stale, coverage eroding | null on all four pre-registered signals |
| 25 agents each building a backend to a specified architecture | wrong dependency direction | 12%, and a checker in the loop took it to 0% |
So: the problem is real (I have watched a developer's own architecture rule get broken in the wild), and it is rare, for agents and humans alike. "Agents will rot your import graph" is a claim I made and have retracted. Nobody has measured what one occurrence costs, so I cannot argue "rare but expensive" either.
The last row is the one that says what archy is for. All 25 unaided agents produced the four layer directories correctly. Every failure was an import going the wrong way: entities reaching down into data access. They got the layout right and the direction wrong, and a directional rule caught all three cases at no cost to the API's behaviour.
That is the shape of the whole thing. Layout is visible in a file tree. Direction, transitive reach and cycles are visible nowhere, at any zoom level, in any single file. And a separate study found that once one of these lands it is never repaired: zero violations were resolved across the sampled corpus, and 2 of 14 repositories sat on broken contracts indefinitely. Rare and permanent, not rare and self-healing.
What that means for what gets built. Feature work premised on "agents will wreck your architecture" went off the table when that premise was retracted, and nothing since has put it back. What survives is narrower and has a number behind it: directional rules, transitive contracts and cycle detection, checked every session. That is a real job and archy does it. What the studies argue against is more of it: five of them produced no evidence that another feature is the missing piece, and the honest reading of headroom-limited results is that the next one will not be either.
So the roadmap that was closed in July stays closed, and being active again did not reopen it. Work here goes into the loop that survived rather than into the list that did not: making what archy already computes arrive where an agent will actually read it, and saying honestly what each answer does and does not cover. Bugs get fixed and contributions are welcome, as before.
The full write-up, including the six measurement artifacts that nearly turned a failed study into a success story, is in docs/WHAT_DIDNT_WORK.md. If you only read one thing here, read that.
The failure it catches

Nothing in that picture is derived. Which layer a module belongs to, and which direction is forbidden, are facts you write down in archy.yaml; archy only checks that the source still agrees with them.
Here is the failure it was built for, compressed into one line. This is archy's own source, under archy's own layer rules, with a single import of the kind an agent adds when it needs a helper and the nearest one is upward:
# src/archy/graph.py
from archy.cli import main # convenience import. The diff looks harmless.
$ uvx archy check .
# 1 layer violation(s) (config: /path/to/archy/archy.yaml)
graph -> cli (forbidden):
src/archy/graph.py:40: archy.graph -> archy.cli (line: 40)
# layer coverage: 10 of 48 modules (21%), 21 of 137 internal edges (15%); 38 module(s) match no layer (`archy check --show-unlayered`)
# 116 scanned module(s) sit outside every declared root package and are not counted above
$ echo $?
1
$ uvx archy cycles .
# 1 cycle(s) found
Cycle of 12 module(s):
- archy.affected
- archy.cli
- archy.conventions
- archy.duplicates
- archy.graph
- archy.headers
- archy.impact
- archy.index
- archy.mcp
- archy.scan_config
- archy.simulate
- archy.watcher
Edges:
src/archy/affected.py:29: archy.affected -> archy.impact (line: 29)
src/archy/cli.py:27: archy.cli -> archy.affected (line: 27)
src/archy/cli.py:34: archy.cli -> archy.conventions (line: 34)
src/archy/cli.py:66: archy.cli -> archy.duplicates (line: 66)
src/archy/cli.py:74: archy.cli -> archy.graph (line: 74)
src/archy/cli.py:82: archy.cli -> archy.headers (line: 82)
src/archy/cli.py:87: archy.cli -> archy.impact (line: 87)
src/archy/cli.py:1537: archy.cli -> archy.index (lines: 1537, 1538, 1566, 1591)
src/archy/cli.py:1511: archy.cli -> archy.mcp (line: 1511)
src/archy/cli.py:111: archy.cli -> archy.scan_config (line: 111)
src/archy/cli.py:113: archy.cli -> archy.simulate (line: 113)
src/archy/conventions.py:72: archy.conventions -> archy.graph (line: 72)
src/archy/duplicates.py:84: archy.duplicates -> archy.graph (line: 84)
src/archy/graph.py:40: archy.graph -> archy.cli (line: 40)
src/archy/headers.py:44: archy.headers -> archy.conventions (line: 44)
src/archy/headers.py:45: archy.headers -> archy.graph (line: 45)
src/archy/impact.py:28: archy.impact -> archy.graph (line: 28)
src/archy/index.py:31: archy.index -> archy.graph (line: 31)
src/archy/mcp.py:117: archy.mcp -> archy.affected (line: 117)
src/archy/mcp.py:119: archy.mcp -> archy.conventions (line: 119)
src/archy/mcp.py:157: archy.mcp -> archy.duplicates (line: 157)
src/archy/mcp.py:166: archy.mcp -> archy.graph (line: 166)
src/archy/mcp.py:182: archy.mcp -> archy.impact (line: 182)
src/archy/mcp.py:202: archy.mcp -> archy.scan_config (line: 202)
src/archy/mcp.py:204: archy.mcp -> archy.simulate (line: 204)
src/archy/mcp.py:205: archy.mcp -> archy.watcher (line: 205)
src/archy/scan_config.py:25: archy.scan_config -> archy.graph (line: 25)
src/archy/simulate.py:46: archy.simulate -> archy.graph (line: 46)
src/archy/watcher.py:35: archy.watcher -> archy.graph (line: 35)
src/archy/watcher.py:36: archy.watcher -> archy.index (line: 36)
$ uvx archy score .
# archy score: 0.636 (0.639 before the edit)
modularity: 0.670 (48 communities, raw Q=0.505)
call-weighted Q=0.699 (calls amplify community structure)
acyclicity: 0.927 (1 cycles, tangle=0.073)
depth: 0.533 (max depth 7)
equality: 0.411 (Gini=0.589)
complexity: 0.767 (2888 functions, cc_mean=2.86, cc_max=46)
# graph: 164 modules, 319 edges (318 before the edit)
# propagation_cost: 0.1089 (diagnostic, not in score; MacCormack reverse-reach fraction)
# calls: 1842 resolved across 262 edge(s), 7.03/edge (diagnostic, not in score)
One import, one edge. A forbidden layer edge, a twelve-module cycle, and the score down 0.003. Nothing in the diff itself says any of that, and no amount of reading the file reveals it, because the rule that makes it a violation is not in the source. You supplied it.
Note the size of the score move. 0.003 is small, and that is the honest shape of this problem: no single edit looks alarming on the number. The cycle count going 0 to 1 and check exiting 1 are the signals that matter here, and the score is what catches the version of this that happens forty times over six weeks. Read docs/SCORING.md before treating the composite as a quality gate.
That example is a direct forbidden import, which is the easy case: an agent that reads archy.yaml first can catch it without archy. The harder and more honest case is a transitive violation, where the edit adds no forbidden import at all and reading the config tells you nothing. docs/WALKTHROUGH.md is a one-command reproduction of that, and it states plainly which archy surfaces catch it (one) and which miss it (three).
Reproduce the example above on a checkout: add that import to src/archy/graph.py, then run the three commands with the uvx prefix. It has to be a separate archy, because that one import is a genuine runtime import cycle, and an editable-installed archy can no longer start to report on itself. archy check exits 1, which is what it does in CI and what the MCP server reports to an agent before it commits.

What archy is not: a code-navigation tool. It will not help an agent find and read code faster; that job belongs to symbol-level, multi-language graph tools like codegraph, and they are better at it. archy answers the other question: you declared this codebase should have these layers, no cycles, and this score; is the agent's edit about to break that, and has the trend been sliding for six weeks? Navigation graphs such as colbymchenry/codegraph carry no such intent, because intent is not in the source, you supply it. Some graph tools do carry declared boundaries (optave/codegraph, tach, agent-arch); what archy adds is narrower, see the analysis below.
The sharp version, about colbymchenry/codegraph (not optave/codegraph, which shares the name and does ship boundary rules), re-checked on 2026-11-09 at v1.6.2: it ships no cycle detection, no config in which to declare layers or forbidden edges, and no command that exits non-zero on a violation. It will happily show you that models imports repositories if you ask the right question. It cannot tell you that is wrong, because wrongness needs a declaration and there is nowhere to put one. Descriptive tools answer questions; archy makes an assertion that breaks the build. The two are both local MCP servers and compose fine; run them together. See docs/research/CODEGRAPH_COMPETITIVE_ANALYSIS.md for the full comparison, including where archy loses and why this distinction is a choice they made rather than a wall they hit.
Start in one command
uvx archy install # detects Claude Code, Cursor, Codex, opencode, Continue and wires each one up
Nothing lands on your PATH: the config it writes runs uvx archy mcp on demand. Prefer a real install? pip install archy, uv tool install archy, or pipx install archy. Either way, try it on a project without installing anything:
uvx archy score . # one-shot architectural health number
uvx archy cycles . # import cycles, Tarjan SCCs plus self-loops
uvx archy check . # layer rules from archy.yaml; exits 1 on violation
Free, MIT licensed, no commercial version planned. One maintainer, Python only. Built by Alex Lee.
Status: v0.55.1, working, installed and maintained. Usable today via:
| Mode | Command |
|---|---|
| Inspection | archy graph, archy cycles |
| CI governance | archy check (reads archy.yaml; --contracts nests the transitive import-linter verdict in the same output, reported, never gated) |
| Transitive contracts | archy contracts (reads .importlinter (canonical) or falls back to archy.yaml; requires archy[contracts]) |
| One-shot score | archy score |
| Trended score | archy score --record + archy trend |
| Refactor priority | archy hotspots (CC x git churn), archy what-to-refactor-next (fused hotspots + edit-risk) |
| Duplicate detection | archy duplicates (two-tier: likely duplicates vs demoted variants - same-class/boilerplate/test/vendored; advisory, not a score axis) |
| House style | archy conventions (naming families by home module, kind families by transitive base, NAME = Ctor(...) registries, mirrored surfaces, a gate inventory kept separate from user-error exits, model census, and the members a project's own __all__ or docs leave out - derived from the source, so an agent stops guessing conventions; advisory, exits 0 on a successful report, and --module NAME switches to a complete, unranked lookup on one module - what it imports, what imports it, and whether it was set aside - which answers a negative the ranked report cannot; --emit-headers derives a per-module block (what it owns, what mirrors it, whether a finding here gates) from that same report and prints it, --write puts it in the module docstring, and --check exits 1 when one is stale) |
| Change coupling | archy coupling (module pairs that co-change in git history but share no import/call edge - hidden dependencies; source-only, advisory) |
| CI impact lookup | archy affected (git diff -> impacted modules + tests, depth-capped) |
| Human-facing export | archy render --view dsm|trend (one self-contained HTML file: no JS, no CDN, offline, byte-stable) |
| Pre-task briefing | archy brief (one screen composing conventions, layer coverage and the gate inventory for an agent to read BEFORE it starts, plus optional --contracts; advisory, always exits 0) |
| MCP server | archy mcp (cached: warm graph builds in seconds even on 10k+ module repos) |
| Parse cache | archy index sync / archy index clear (persistent .archy/index.db; transparent under the MCP server) |
| Agent install | archy install / archy uninstall (auto-detect Claude Code, Cursor, Codex, opencode, Continue; wire in or cleanly remove the MCP server) |
How the score is computed and how to read it: docs/SCORING.md. Benchmarks against pydantic, fastapi, flask, pytest, and archy-on-archy: docs/CASE_STUDIES.md. Design rationale and comparison with sentrux: docs/LEARNINGS.md.
In the wild
ADOPTERS.md is empty and no issue has yet been filed by anyone but me. Outside pull requests are a different story and recent: three landed on 2026-07-25, two merged. Good-first tickets are labelled and deliberately left for others.
If you are running archy on a real codebase I would like to hear what it found, especially if the answer is "nothing useful" - that answer is now supported by measurement rather than merely possible.
Why
The failure at the top of this page is the whole reason archy exists: I wanted a single number per commit that would have caught it.
AI agents generate code at machine speed, and the reasoning went: without a feedback loop on structural health (module coupling, import cycles, layer violations), codebases drift architecturally even when every individual change looks fine in review.
That reasoning is the part I tested and could not support. Twenty-five agent runs produced zero structural regressions, and human commits break their own declared rules on 0.66% of commits. The drift may still be real over long horizons, which is not what a per-edit measurement can see, but I have no evidence for it and I am not going to assert it. The rest of this section is the case as I originally made it, kept because the citations are accurate even where my inference from them was not.
Where a feedback loop did pay is narrower, and it is the moment code is written rather than the patrol afterwards. Building a new backend to a specified architecture, 3 of 25 unaided agents got the dependency direction wrong; with a checker in the loop, none did, and the API behaved just as well. That is one model, one framework, and the mildest of the Constraint Decay paper's conditions, so it is not a general claim. It does say the loop is worth having at generation time, where the mistake is cheap to prevent and, per the decay study, never repaired afterwards.

What that buys you is placement: it runs in CI, in pre-commit, and as an MCP server (archy mcp), so a coding agent can read its own architectural impact before it commits rather than after review.
The agent-feedback framing is empirically supported by 2025-2026 research: the Navigation Paradox paper shows large LLM context windows do not eliminate the need for structural graph navigation, LocAgent's ablation finds graph edges materially improve code-localization accuracy, the Constraint Decay paper (arxiv:2605.06445) finds agents lose ~30 points in pass rate as architectural constraints accumulate (Clean Architecture layering alone costs -9.1 points, on the open and mid-tier models tested) and that its ground-truth layer/dependency-direction verifier is essentially archy check, and the coding-agent failure-mode literature names the specific patterns (scope drift, cross-file reasoning failure) that an architectural feedback loop is built to catch. Citations, a failure-mode-to-archy-capability mapping, and the resulting roadmap priorities are in docs/research/RESEARCH_METRICS.md §14c.
The underlying mechanism
Beneath the empirical case is a structural one. Anthony Hobday, writing about software quality, names it precisely: "as the number of things goes up, the number of relationships goes up even faster. Eventually it's impossible for people to properly consider all of those relationships." Coherence is the state where those relationships still hold together; entropy is its steady loss as a system grows. A single author keeps a codebase coherent by remembering every edge. An agent generating code at machine speed cannot, and neither can a team past a certain size.
That relationship load is exactly what archy reads. Coupling, the DSM, import cycles, and change-coupling are all measures of how far the graph has drifted from "one person can hold it in their head." archy externalizes that memory into a number and a trend, so the growth in relationships stays visible instead of being discovered during a refactor that blows up.
Scope
- Python only. The cross-language story belongs to sentrux; that division is settled. archy goes deep on Python (transitive contracts, SDP, NCCD,
if TYPE_CHECKING:handling inarchy cycles) rather than broad across languages; seedocs/LEARNINGS.md§"Competitive landscape". - Tree-sitter powered. Robust to in-flight edits and partial files; survives syntax errors that would crash
ast. - Score that trends over time. A single number per commit, persisted, plotted. Trend matters more than the absolute value.
- Rules as YAML. "Layer X cannot import Y." No DSL, no plugins (yet).
Non-goals
- Multi-language analysis
- Replacing linters, type checkers, or test runners
- Generating code or auto-fixing violations
archy MCP questions
How do I add archy MCP to Claude Code?
Run claude mcp add archy -- uvx archy in your terminal. Add --scope user to make it available in every project.
How do I add archy MCP to Cursor?
Open ~/.cursor/mcp.json and add the server under mcpServers, using the Cursor config shown in the install section above.
What does archy do?
It parses a Python project's imports and calls to answer questions about change reach, affected tests, dependency cycles, and adherence to declared architectural layers.
How does archy handle architectural rules?
Users define their architectural facts, such as which module belongs to which layer and which directions are forbidden, in an archy.yaml file.
Can archy be used in a CI/CD pipeline?
Yes, it runs as a CLI that can be used in CI to enforce layer governance and catch violations.