Seam parses a repository with tree-sitter, stores it as a typed graph in local SQLite, and exposes it over twelve read-only MCP tools — so an AI agent queries structure instead of reconstructing it with grep every session.
An agent that greps to answer "what breaks if I change this?" rebuilds a call graph by hand, every session, from scratch. That graph already exists in the parser. Seam computes it once and answers the question directly.
grep init_db → 14 files → open each → trace imports → reconstruct the graph by hand. ~30k tokens, often wrong.
seam_impact init_db → blast radius bucketed by risk tier, graph-accurate, with provenance. ~4.5k tokens.
Source is parsed into a SQLite graph; two post-passes enrich it (clusters, synthesis); a watcher keeps it fresh. Reads fan out to the MCP server, the CLI, and the web explorer — all over the same engine.
A lower layer never imports an upper one. The analysis layer is built from pure leaf modules — stdlib only, no DB, no IO, never raise — so each algorithm is testable in isolation and the read path's failure surface is tiny.
No database, no IO, never raise — degrade to an empty/neutral result on error.
clustering · rwr · relevance · byte_budget · steer · staleness · builtins · imports
server/tools.py re-exports every handler so all imports stay byte-identical, while the implementation lives in focused sub-1000-line files: impact_handler, trace_handler, handler_common.
Edges are keyed by symbol name, not row id — which is exactly what lets the watcher re-index one file without rewriting the rest of the graph. Traversal is kind-agnostic, so every tool sees every edge kind for free.
| Edge | Captures |
|---|---|
| call | one symbol invokes another |
| import | a module / symbol import |
| extends | subclass → base class |
| implements | class → interface |
| instantiates | new Foo() / struct literal |
| holds | class stores a typed field (composition) |
| uses | function references a type as a parameter |
| reads | a field is read (data-flow) |
| writes | a field is written / deleted |
| http_calls | literal local client calls → route symbols · no dynamic URL guessing · direct provenance |
| reads_config | code reads a literal config/env key |
| configures | a config key describes a runtime resource |
Confidence is recomputed at read time against the live name-count map — so a single-file watcher edit keeps the whole graph correct with no backfill.
| 1 · import | same-file import binds target to one declarer → EXTRACTED (the homonym fix) |
| 2 · name-count | count==1 → EXTRACTED · count>1 → AMBIGUOUS + proximity best_candidate |
| 3 · builtin | fires only at count==0 — a user-declared name can never be misread as builtin |
| 4 · fallback | count==0, not builtin → INFERRED / unresolved |
The server never writes the index. Each tool answers one question an agent actually asks.
| EXTRACTED | target is unambiguous — high certainty |
| AMBIGUOUS | name collides — verify by reading |
| INFERRED | heuristic / cross-module / synthesized |
A multi-hop path is only as strong as its weakest hop.
| WILL_BREAK | d=1 · direct dependents — must update |
| LIKELY_AFFECTED | d=2 · indirect — should test |
| MAY_NEED_TESTING | d≥3 · transitive — test if critical |
seam_changes rolls these up to low → medium → high → critical.
Loaded packaged-first from the wheel and auto-migrated additively on open. Gitignored — never leaves the machine.
| Table | Holds |
|---|---|
| files | indexed files · hash · mtime · indexed_at |
| symbols | nodes · kind (incl. field, route, config, resource) · qualified_name · signature · visibility · cluster_id · entry_score |
| edges | source · target · kind (15) · confidence · receiver · synthesized_by · provenance |
| routes | HTTP route metadata · method · path · framework · handler · provenance |
| config_keys | config/env key metadata · source · role · redacted value shape · provenance |
| resources | runtime resource metadata · category · source · provenance |
| comments | WHY / HACK / NOTE / TODO / FIXME markers |
| clusters | Louvain communities · label · size · cohesion |
| import_mappings | per-file import bindings (read-time promotion) |
| embeddings | optional semantic vectors (--semantic only) |
| symbols_fts | FTS5 over name + docstring + signature + search_text |
off that is byte-identical to before. Schema changes are additive migrations that auto-run on open.