rift
DeveloperProtocol

Core

The core models describe source files and the facts providers return about them. The reference links their generated schemas.

Paths and files

ProjectPath is a normalized UTF-8 path relative to the workspace root. It uses /, contains no . or .. component, and cannot name .rift, where the server stores its own state. Normalized means Unicode NFC.

The index skips symlinks. A linked file answers under its own path when the source policy includes it.

Source locations

TextRange is a pair of half-open UTF-8 byte offsets. Each provider converts its toolchain's positions to byte offsets, so ranges from different providers use one scale.

nodes returns the syntax nodes covering one byte position, outermost first. Each node carries its grammar kind and byte range.

Source discovery

Source resolvers run before syntax or semantic analysis. They inspect workspace files, manifests, and lockfiles, then publish a source catalog of the project's source units and the packages the project depends on. A resolver reads no package source, downloads no package, and does not change the environment.

SourceUnitId names one unit in that catalog, and a package file the global index answers from takes the same form: rift://source/cargo/crates.io/demo@1.0.0/src/lib.rs. This package address names its manager, registry, package and exact served version, then the physical path in that release. A project source address uses rift://source/project/ followed by its project-relative path. Logical symbol identities remain separate from these physical addresses.

The key is never a digest. A Digest reaches the wire only as a short witness where byte identity matters: the first eight hex characters of a SHA-256, the same convention NodeId's witness uses. The server computes and compares the full digest internally, so it never travels whole.

SourceLocationKind names where a declaration's source belongs: project, dependency, standard library, or external source. SourceKind names how the source came to exist: authored, generated, or synthetic for a declaration with no source unit. The two stay separate because generated code can belong to the project or to a dependency.

A declaration in that package file carries both in its SymbolOrigin, beside the package that owns it:

{
  "location": "dependency",
  "package": {
    "manager": "cargo",
    "registry": "crates.io",
    "name": "demo",
    "version": "1.0.0"
  },
  "source_kind": "authored"
}

A resolver publishes a new catalog revision when files, manifests, lockfiles, version pins, or generated-source metadata change, and fact providers publish a new revision after they analyze one. A read whose scope reaches packages reports a resolver that read less than the workspace states as a package_context_degraded ReadWarning, naming the resolver by its manager name, such as cargo or npm.

Semantic facts

Providers return facts in families: symbols, the syntax nodes that write them, relationships between symbols, resolved types, diagnostics, and history. A family no provider can produce refuses with capability_unavailable. Signatures and documentation travel on the symbol itself, covered under the types family.

A search result carries the answer, pagination, and warnings. Exact symbol and source reads carry an outcome and immutable view; a symbol read can carry a declaration_cursor. A condition the caller must weigh - an index that lags the tree - arrives as a typed entry in warnings; the Reads section in the MCP reference states the paging and warning contract.

SymbolOrigin keeps source location separate from source kind. A synthetic symbol carries neither a location nor a package; dependency and standard-library symbols can carry facts even when their provider cannot return source bytes. The declaration's own source-catalog unit, where its location differs from the project, rides on a get_symbol declaration binding, addressed by unit instead of the path a project declaration carries. The logical symbol identity remains separate from that physical source address.

History facts

The history family is resolved from the workspace's version-control history rather than from the current tree. A SymbolHistory is one symbol's timeline - each SymbolVersion names the RevisionId that touched it and what the revision did: introduced it, changed its body, changed its signature, moved it, removed it.

The history provider correlates declarations across revisions using their parsed structure and surrounding changes. A SymbolId can change when a language includes a module path in its qualified name; the timeline still records the move.

The walk is bounded by the configured history depth, and the timeline rides a get_symbol result when the request asks for history.

On this page