LSP, SCIP & LIP

These three protocols are often described as alternatives. They aren’t — they solve different problems at different points in the development lifecycle.


The one-sentence version

LSP — answers questions about the file you have open right now, inside your editor.
SCIP — produces a precise, compiler-verified snapshot of a whole repository for audit and analysis.
LIP — maintains a live, incrementally updated graph of the entire repo that AI agents and tools can query at any time.


LSP

LSP (Language Server Protocol) is a request-response protocol between an editor and a language server. The language server runs in the background and answers questions about the file the cursor is in.

Strengths:

  • Works without any setup beyond installing a language server
  • Sub-second response for in-file queries (hover, completion, inline diagnostics)
  • Supported by every major editor out of the box

Limitations:

  • Stateless per-session: the language server restarts cold when the editor restarts
  • Scoped to open files: workspace-wide queries (workspace/symbol) are best-effort and often incomplete
  • No cross-session persistence: annotations, notes, and derived facts vanish on restart
  • No blast-radius concept: the server can tell you where verifyToken is defined, not which files break if its signature changes
  • Fire-and-forget sync (textDocument/didChange) is a known source of client/server drift

LIP’s LSP bridge:
lip lsp is a standard LSP server that proxies queries to the LIP daemon. Editors see it as a normal language server; no plugin needed. The difference is that definitions, references, and workspace symbols are served from LIP’s live persistent graph — they’re available instantly on editor open and remain accurate across restarts.


SCIP

SCIP (Source Code Index Protocol, developed by Sourcegraph) is a protobuf-based format for a complete, compiler-verified index of a repository. You run a SCIP indexer (e.g. scip-rust, scip-typescript) as part of CI, and the result is a binary .scip file you query offline.

Strengths:

  • Compiler-accurate precision: SCIP is generated by running the actual compiler or type-checker. Generics are resolved. Overloads are disambiguated. Trait bounds are checked. Every symbol reference is exact.
  • Full data-flow support: The SCIP ecosystem feeds into Code Property Graphs (CPGs) which support taint analysis.
  • Stable cross-package symbol identifiers: SCIP uses a deterministic URI scheme that survives refactors and package renames.
  • Language coverage: Indexers exist for Rust, TypeScript, Python, Go, Java, Kotlin, C, C++, C#, and more.
  • Compliance and audit: Because the snapshot is immutable, it can be stored, diffed against previous runs, and attached to releases.

Limitations:

  • Staleness: A SCIP index is a snapshot taken at index time. One changed function signature makes the blast-radius data stale until the next full re-index.
  • Re-index cost: On a large repository, a full SCIP pass takes 30–90 minutes.
  • No live updates: There is no incremental sync protocol. You re-run the indexer, you get a new file.
  • No annotations: SCIP has no way to attach persistent notes or labels to symbols that survive across index runs.

LIP + SCIP together:
lip import --from-scip index.scip ingests a SCIP artifact and upgrades all its symbols to confidence 90, including type signatures extracted from SCIP’s documentation (SCIP indexers place the rendered signature as documentation[0]). From that point LIP maintains freshness incrementally — SCIP never needs to run again unless you want a new full-precision baseline or CPG data-flow analysis.


LIP

LIP (Linked Incremental Protocol) is a persistent daemon that keeps a live query graph of the entire repository and updates it incrementally as files change.

Strengths:

  • Always current: every file save updates the affected symbols in <1 ms
  • Blast radius: first-class concept — tracks a reverse dependency graph per symbol, computes direct and transitive dependents, assigns a risk level
  • Persistent annotations: symbols can carry key/value annotations (lip:fragile, team:owner, agent:note, lip:nyx-agent-lock) that survive file changes and daemon restarts
  • Federated dependency slices: external packages are content-addressed blobs indexed once by anyone on the team
  • Batch API: BatchQuery runs N queries under a single db lock — one round-trip instead of N
  • AI-native: MCP server, agent-lock annotations, SimilarSymbols fuzzy search, semantic embeddings
  • Progressive results: Tier 1 (tree-sitter, <1 ms) available immediately; Tier 2 (compiler) upgrades in the background

Limitations — where LIP is weaker than SCIP:

CapabilityLIP Tier 1LIP Tier 2SCIP
Type precisionSyntactic onlyCompiler-level (all 4 languages)Compiler-level (all languages)
Type signatures on symbolsNoYes — hover + SCIP importYes
Cross-file type definition linksNoYes — all 4 languagesYes
Overload resolutionNoRust onlyYes
Generics / trait boundsNoRust onlyYes
Data-flow / taintNoNoYes (full CPG)
Tier 2 language coverageRust, TypeScript, Python, DartRust, TypeScript, Python, Dart15+ languages
Local variable type inferenceNoRust (inlay hints)No — SCIP does not index locals
Cross-file reference precisionName-based heuristicResolved (all 4 languages)Exact, all languages
Confidence score30–5090~100

The gap in plain terms: LIP Tier 1 uses tree-sitter — a syntax parser. It knows that fn verifyToken exists and that some code nearby calls something named verifyToken, but it cannot prove they’re the same symbol across files without the name index. SCIP runs the compiler, which resolves that unambiguously.

LIP Tier 2 closes this gap for Rust (rust-analyzer), TypeScript (typescript-language-server), Python (pyright-langserver / pylsp), and Dart (dart language-server). All four backends are lazy-initialized on first use and degrade gracefully if the binary is not in PATH. As of v1.4, Tier 2 also records cross-file type definition relationships (textDocument/typeDefinition) for all four languages, and rust-analyzer captures local variable types via inlay hints — coverage that SCIP does not provide.


Decision guide

You need…Use
Editor hover, completion, inline errorsLSP (language server of your choice)
Always-current blast radius during developmentLIP
Cross-file references during an active refactorLIP
AI agent planning before a changeLIP (lip_batch_query)
Persistent notes on symbols across sessionsLIP (annotations)
Precise data-flow / taint analysisSCIP → CPG
Compliance snapshot for a releaseSCIP
Nightly full-precision baselineSCIP → lip import
Type-precise go-to-definition for TypeScriptLSP (tsserver) or SCIP

Using all three together

The layers compose without conflict:

Source files

    ├── tree-sitter (Tier 1, &lt;1 ms/file)
    │        │
    │        ▼
    │   LIP Daemon  ─── live graph, blast radius, annotations
    │        │
    │        ├── LSP bridge  ─── editors (standard LSP, no plugin needed)
    │        ├── MCP server  ─── AI agents (Claude Code, Cursor, CKB, …)
    │        └── lip import  ←── SCIP artifact (nightly CI)

    └── SCIP indexer (CI, on schedule)

              └── CPG / taint tools  ─── compliance, security audit

Concrete example — CKB:

CKB’s analyzeImpact historically required a SCIP refresh to be current. With LIP as its CallerProvider:

  • analyzeImpact reads from LIP → always current, no re-index
  • CKB’s risk score, telemetry overlay, ADR linking, and documentation references sit on top of LIP’s raw graph data — unchanged
  • SCIP stays as the source for data-flow queries that LIP Tier 1 cannot answer
  • LSP language servers enrich individual symbol type details

Each tool does what it’s best at. Nothing is replaced.


Remaining gap versus SCIP

The remaining gaps after v1.5:

  • Data-flow / taint analysis — requires full CPG; not in scope for LIP’s current architecture. SCIP remains the right tool for taint tracking and security audit workflows.
  • Generics / trait bounds / overload resolution for TypeScript, Python, Dart — rust-analyzer exposes this via Tier 2 for Rust. The other three languages rely on hover text parsing; deep generic instantiation is not yet extracted as structured relationships.
  • Language coverage — Tier 2 covers 4 languages; SCIP indexers exist for 15+. Go, Java, Kotlin, and C# are on the v2.0 roadmap.

Where LIP now exceeds SCIP (v1.6):

  • Local variable type inference (Rust) — rust-analyzer inlay hints give LIP inferred types for local bindings inside function bodies. SCIP indexers do not index locals.
  • Freshness — LIP updates in <1 ms per save; SCIP is a snapshot that ages between CI runs.
  • Symbol annotations — persistent key/value labels that survive file changes and daemon restarts; SCIP has no equivalent.
  • Push notificationsIndexChanged events are broadcast to all active sessions after every upsert, enabling instant cache invalidation without polling. SCIP has no equivalent.
  • Batch APIsBatchQueryNearestByText, BatchAnnotationGet, and BatchQuery collapse N round-trips into one, under a single db lock where possible. SCIP is an offline file format; there is no query API to batch against.
  • Symbol-level semantic searchQueryNearestBySymbol embeds a symbol’s description on demand and searches across the per-symbol vector store. SCIP has no embedding layer.
  • Protocol version negotiationHandshake / HandshakeResult lets clients detect daemon/client version drift at connect time and degrade gracefully.
  • Pairwise similaritySimilarity returns the cosine similarity between any two stored embeddings (file or symbol) in a single round-trip. SCIP has no query API.
  • Query expansionQueryExpansion embeds a short query string and returns the nearest symbol display names. Useful before a workspace-symbol search when the exact name isn’t known.
  • Embedding clusteringCluster groups a set of file or symbol URIs by embedding proximity within a given radius, enabling topic-based navigation without any external vector DB.
  • Raw vector exportExportEmbeddings returns stored vectors directly for external re-ranking, visualization, or integration with other tools.
  • Targeted re-indexReindexFiles forces a re-index of specific files from disk, bypassing the directory scan. Useful after selective git checkout or CI-generated file changes.

For compliance snapshots, data-flow analysis, or audit workflows, SCIP remains the right tool. Feed SCIP artifacts into LIP with lip import --from-scip to get a full-precision baseline (including type signatures extracted from SCIP’s documentation), then let LIP maintain freshness incrementally.