luqueee.
All projects
/ 01 Go · MCP

Kivgraph.

Local code intelligence for coding agents, with exact symbol references and cross-repository change impact.

Beyond searching for names

A text search can find validate. It cannot, by itself, distinguish two unrelated declarations with that name, follow an implementation through an interface, or prove which other repositories depend on the declaration you want to change.

Kivgraph is a local code-intelligence server for coding agents. It analyzes registered repositories, gives symbols stable identities, and publishes a semantic graph that an agent can query through MCP. The goal is structured navigation and change-impact analysis—not a map of every HTTP request, queue or database connection in a running system.

Source code becomes a canonical graph

The input is a configured corpus, not whatever files an agent happened to open. Repository registration identifies source roots; indexing discovers language-specific analysis units, runs their analyzers, normalizes the resulting facts, and merges the complete corpus.

Source repositories become a canonical semantic graphRegistered source repositories are analyzed by language-aware engines. Normalized symbols and relationships are written to canonical LadybugDB storage. Queries use a separately published immutable snapshot.Repositoriesregistered source rootssource filesLanguage enginesresolve code relationshipsnormalizeSemantic factssymbols, edgesidentity + provenancepersistLadybugDBcanonical graphnot the query pathCode relationships, not inferred network traffic.
The write path · a full corpus rebuild, with reusable analysis facts.
Implementation on main

Symbols, relationships, confidence and provenance enter a fresh LadybugDB generation, the canonical persisted graph. The database is not traversed for each MCP relationship query: a separate immutable snapshot is the read model.

Indexing is a full rebuild of the registered corpus. A validated fact cache can reuse unchanged analysis units, but that reuse is not an incremental mutation of the graph already serving readers. Cache admission checks analyzer compatibility and the recorded content, provider and registry inputs.

Different languages, one fact contract

The language engine supplies meaning; the common model supplies consistent identities and relationships. Select a language below to see where its facts come from.

Go analysis adapterGo resolves declarations and relationships using compiler type information, not matching names.Go sourceregistered repositoryanalyzego/typesCompiler type informationnormalize factsCanonical modellanguage-independent factsconfidence stays explicit

Go resolves declarations and relationships using compiler type information, not matching names.

The semantic boundary · each engine supplies evidence, not just matching names.
Implementation on main

Go uses go/packages with syntax and type information. TypeScript uses the native compiler's asynchronous Program/Checker API in a Node.js worker. Rust runs rust-analyzer scip for a Cargo workspace. Dart combines language-server symbols with Analysis Server navigation and semantic events. Python can use the bundled AST fallback or an explicitly selected Pyright-based exact mode.

These are not interchangeable guarantees. Python's inferred fallback references remain CANDIDATE; stronger proof receives explicit grades such as EXACT_TYPECHECKED, EXACT_PACKAGE_MAPPED or STRUCTURAL_CERTAIN. The dispatcher also supports configured Java and C# semantic adapters. Toolchain availability matters: an unavailable analyzer can leave a language without indexed facts, so coverage must be inspected rather than assumed.

Crossing repositories requires an owner

An import name is not enough to establish a symbol-level relationship. Kivgraph has to identify the provider repository, map the resolved declaration into that provider, and verify that its target exists in the merged corpus.

A cross-repository edge requires a proven owner and indexed targetA consumer occurrence is resolved using its language engine and provider identity. A target must belong to the registered provider and exist in the merged corpus. Missing or ambiguous proof yields an unresolved record, not a guessed edge.Consumer occurrencea use inside a declarationconsumer owns the evidencelanguage resolutionResolve providerregistered repository identitypackage / module / crate mappingverify targetMerged corpusowning declaration indexed?identity actually present?provenotherwiseResolved edgeexplicit proof gradeUnresolved factreason, not a guessed target
Cross-repository resolution · a provider identity is not yet a proven symbol edge.
Implementation on main

Go attribution follows module and workspace resolution. TypeScript resolves the owning provider project, including declaration-map or provider-export paths; a re-exporting package need not own the declaration. Rust checks crate identity and the requested version. Ambiguous providers, missing source mappings or unindexed target declarations must not turn into guessed exact edges.

The consumer owns the occurrence evidence; the provider owns the destination declaration. Failed resolution becomes a separate unresolved fact with a reason. A package-level dependency can be known even when a symbol-level edge cannot be proven.

Prepare privately, publish a whole generation

The rebuild loads candidate canonical storage, derives a query snapshot from that stored graph, and checks integrity, counts, probes and source observations before publication. Source re-observation detects changes during indexing; it does not lock the repositories.

Illustrative publication · no real indexing or database

A candidate generation is separate from the graph readers useGeneration 1 is published. Prepare another generation to see the separation.PREPARING A REPLACEMENTSERVING READERSCanonical graphLadybugDBbuildCandidatenext immutable snapshotprepared by indexingpublish replacementPublished snapshotgeneration 1immutable read modelqueryReadersMCP queriespublished graph onlyNo per-query database traversal.

Generation 1 is published. Prepare another generation to see the separation.

The publication boundary · building a candidate does not replace the graph readers use.
Implementation on main

Successful publication makes a durable generation current, then installs a strictly newer snapshot in the reader store. Existing readers can retain the old immutable snapshot while subsequent reads acquire its replacement. The controls above illustrate this boundary; they do not run Kivgraph or pretend to measure indexing progress.

A serving process can map a published snapshot.kvsnap after validating its graph-content digest. If that representation is unavailable or rejected, it can reconstruct the read model from readable canonical storage. That fallback is not a promise that a broken database remains queryable.

Generation following notices publication by another process. Optional source watching batches changes and marks stale profiles for another full rebuild; branch-change resynchronization is a separate trigger. Neither path edits the graph underneath an active query.

What an agent actually reads

An agent talks STDIO to kivgraph serve. That entry can run the MCP server locally or relay to a shared daemon; a complete graph-owning process per client is not mandatory. Graph loading is deferred until needed, and a missing generation exposes repair/indexing controls rather than pretending relationship queries are available.

MCP relationship queries traverse the published snapshotClient-facing STDIO reaches a local server or a shared daemon through a relay. Relationship tools query immutable snapshot adjacency and return located rows with confidence and completeness.Coding agentMCP clientSTDIOKivgraphlocal serve or relayto shared daemonresolve + queryPublished snapshotsymbol identitiesindexed relationshipslocated rowsGraph responserepository, path, rangeconfidence + completeness
The read path · indexed relationships and current source bodies are different data.
Implementation on main

Relationship tools resolve a selector against a snapshot, traverse its indexed adjacency, apply confidence rules and return addressable rows. References, implementations, outward dependencies, cross-repository consumers and blast radius answer different directions of the same graph. Symbol lookup, intent search, outlines and repository/status tools help an agent choose where to investigate next.

Rows carry repository, relative path, qualified symbol name and declaration range. That range identifies the containing declaration, not necessarily the precise reference token. Generation-bound cursors prevent a later page from silently mixing snapshots.

Answers also distinguish COMPLETE from LOWER_BOUND according to recorded unresolved references and unreadable scopes. “Complete” is about the indexed scope—not unregistered repositories or arbitrary live source changes. Search guidance accompanying a lower-bound answer is advice, not an automatically executed fallback search.

get_source takes a different path after symbol resolution: it reads current repository files and compares their bytes with the indexed digest. It enforces path restrictions and output limits, and reports stale or trimmed bodies. Stale reanchoring is a textual heuristic, not fresh semantic analysis and not a relationship update.

Indexing is a separate, consent-gated operation

The MCP surface declares thirteen read-only tools and two mutations, index_project and start_index_project. The mutation tools request consent through client elicitation or explicit confirmation before repository registration and indexing. Registering a batch results in one full corpus rebuild in a child process.

The asynchronous path returns an operation ID; get_index_status reports progress and a terminal result. Its registry is process-local, with one active operation and bounded completed history—not a durable job queue. A shared daemon can share it across sessions. Indexing failure and failure attaching an already-published generation are distinct boundaries; publication is not simply undone because a later attachment failed.

The viewer does not call MCP

kivgraph ui is another local surface with its own snapshot store and generation follower. The browser requests graph data over HTTP; it is not issuing MCP tool calls.

The graph viewer is a separate read-only HTTP surfaceThe browser uses kivgraph ui HTTP APIs, not MCP. Graph tiles and neighborhoods use a published snapshot; topology also reads published source observations and invalidation metadata. Mismatched generation pins are rejected.Browserread-only 3D viewerGET / HEADkivgraph uiHTTP API + web assetsnot an MCP clientgraph readsPublished snapshotsymbols + graph tilesgeneration-pinned layoutPublication metadatasource manifest + profilesinvalidation / freshnesstopologyUse an explicit loopback bind for a private local viewer.
The visual surface · independent read-only HTTP, not an MCP frontend.
Implementation on main

The HTTP API accepts GET/HEAD. Symbols, neighborhoods and binary graph tiles use the published snapshot, with layouts cached by generation. Topology additionally reads profile configuration, published source observations and invalidation metadata. A mismatched generation pin is rejected instead of quietly combining old and new graph data.

Read-only does not mean private: the viewer has no authentication and its default bind is network-wide. For local exploration, bind explicitly:

kivgraph ui --addr 127.0.0.1:7777

Release bundles package the Go server, native LadybugDB library, workers and optional viewer assets; language runtimes and SDK requirements still apply. The documentation/landing site deploys separately. Neither that site nor the 3D renderer owns semantic resolution.

A research result, not a token-saving promise

Kivgraph began by asking whether semantic navigation could reduce the context coding agents need. Some isolated queries beat searching and reading files, but the project's end-to-end agent benchmarks did not demonstrate reliable net token savings across long conversations. Rare names in small repositories may still be cheaper to find with a simple search.

Its lasting value is precise navigation, explicit uncertainty and cross-repository impact analysis—not a claim that every graph query is cheaper or every empty result proves absence everywhere.

Read the user documentation · Explore the source and benchmarks

Back to all projects