Skip to main content
Graph slicing is how SDL-MCP answers the question: “which symbols are relevant to this task?” Instead of reading files in the same directory, it follows the dependency graph. Starting from the symbols most relevant to a task, it traverses call and import edges outward, scores each candidate by relevance, and returns the N most important symbols within a token budget.

Why Directories Are the Wrong Abstraction

Code doesn’t respect directory boundaries. A function in src/auth/validate.ts might depend on src/db/queries.ts, src/config/types.ts, and src/util/hashing.ts. Directory-based context misses all of these cross-cutting relationships and floods the window with irrelevant sibling files instead. A graph slice follows the actual connections.

How Slicing Works

     Your Task: "Fix the auth middleware"


           ┌──────────────┐
           │ Entry Symbols │  ← auto-discovered from taskText
           │ authenticate  │     or explicitly provided
           │ validateToken │
           └──────┬───────┘

          BFS / beam search
          across weighted edges

    ┌─────────────┼─────────────┐
    │             │             │
    ▼             ▼             ▼
 ┌──────┐   ┌──────────┐   ┌─────────┐
 │hashPw│   │getUserById│  │JwtConfig│   ← call weight: 1.0
 └──┬───┘   └────┬─────┘   └────┬────┘     import weight: 0.6
    │            │              │            config weight: 0.8
    ▼            ▼              ▼
 ┌──────┐   ┌────────┐   ┌──────────┐
 │bcrypt│   │dbQuery │   │envLoader │   ← frontier (just
 └──────┘   └────────┘   └──────────┘     outside the slice)

 ═══════════════════════════════════════
        Token budget reached.
        8 cards returned (~800 tokens)
        vs. reading 8 files (~16,000 tokens)

Edge Weights

Not all relationships indicate equal relevance. SDL-MCP weights edges by how strongly they signal that the connected symbol matters to the task:
Edge TypeWeightRationale
Call1.0If A calls B, B is almost certainly relevant to understanding A
Config0.8Configuration dependencies are important but less direct
Import0.6An import indicates awareness, not necessarily deep relevance

Symbol Scoring

Each symbol in the BFS frontier is scored by a weighted combination of signals:
SignalWeightDescription
Query relevance0.40Search term overlap from taskText
Stack trace locality0.20Proximity to parsed stack trace entries
Hotness0.15Composite of fan-in, fan-out, and churn metrics
Structure0.15File path structural specificity
Kind0.10Symbol kind (functions/methods scored higher than variables)
Cluster cohesion+0.15 boostSame-cluster symbols; +0.05 for related clusters
Low-confidence call edges can be filtered via minConfidence or minCallConfidence to keep the slice tight and prevent noise from ambiguous resolutions.

Token Efficiency

ApproachSymbols CoveredToken Cost
Reading 8 files directly8 files~16,000 tokens
Graph slice (8 cards)8 symbols + graph structure~800 tokens
The slice returns the most relevant symbols — not arbitrary file contents — at 20x lower token cost.

Auto-Discovery Mode

You don’t need to know symbol IDs. Pass a taskText string and SDL-MCP automatically discovers the best entry symbols:
{
  "repoId": "my-app",
  "taskText": "fix the authentication timeout bug",
  "budget": { "maxCards": 30, "maxEstimatedTokens": 4000 },
  "includeRetrievalEvidence": true
}
SDL-MCP supports two discovery paths depending on index health: Hybrid retrieval (when semantic.retrieval.mode: "hybrid" and indexes are healthy):
  1. Run a single hybrid search combining full-text search (FTS), vector similarity, and Reciprocal Rank Fusion (RRF)
  2. Score and rank candidates across all retrieval sources
  3. Build the slice from the top-ranked seeds
Legacy fallback (when hybrid is unavailable):
  1. Token-by-token searchSymbolsLite fan-out across the task text
  2. Score and rank individual matches
  3. Build the slice from the top-ranked seeds
The hybrid path produces better seed quality because it understands meaning via vector search rather than just token overlap, and it runs a single efficient query instead of per-token fan-out. When includeRetrievalEvidence: true is set, the slice response explains how seeds were discovered:
{
  "retrievalEvidence": {
    "mode": "hybrid",
    "symptomType": "taskText",
    "candidateCountPerSource": {
      "fts": 28,
      "vector:all-MiniLM-L6-v2": 24
    },
    "fusionLatencyMs": 8,
    "fallbackReason": null
  }
}
The symptomType field classifies the input: "taskText", "stackTrace", "failingTest", or "editedFiles".
You can also pass stackTrace, failingTestPath, or editedFiles instead of taskText to seed the slice from different kinds of context signals.

Slice Lifecycle

Slices aren’t one-shot. They have a full lifecycle with handles, leases, and delta updates:
  slice.build        slice.refresh       slice.spillover.get
  ──────────►  handle  ──────────►  delta  ──────────►  overflow
                 │                    │                     │
                 │   lease expires    │   nothing changed   │   no more pages
                 ▼                   ▼                     ▼
              rebuild            notModified              done
1

Build the slice

sdl.slice.build creates the slice, traverses the dependency graph, scores candidates, and returns the top symbols within budget. The response includes a handle (opaque reference) and a lease (expiry time).
{
  "tool": "sdl.slice.build",
  "arguments": {
    "repoId": "my-app",
    "taskText": "fix the authentication timeout bug",
    "budget": { "maxCards": 30, "maxEstimatedTokens": 4000 }
  }
}
2

Refresh the slice (delta-only)

sdl.slice.refresh returns only what changed since your last version — dramatically cheaper than rebuilding. If nothing changed, the response is { "notModified": true }.
{
  "tool": "sdl.slice.refresh",
  "arguments": {
    "repoId": "my-app",
    "handle": "<handle from build>",
    "knownCardEtags": { "validateToken": "a7f3c2..." }
  }
}
3

Page through spillover

sdl.slice.spillover.get pages through symbols that scored well but didn’t fit within the budget. Call it repeatedly until hasMore is false.
{
  "tool": "sdl.slice.spillover.get",
  "arguments": {
    "repoId": "my-app",
    "handle": "<handle from build>",
    "page": 1
  }
}

Wire Format Efficiency

Slices support three compact wire format versions for minimal bandwidth:
VersionEncodingBest For
V1Shortened field namesBackward compatibility
V2 (default)+ deduplicated file paths and edge type lookup tablesMost use cases
V3+ grouped edge encodingLarge, edge-dense graphs
Combined with ETag-based conditional cards (knownCardEtags), a slice refresh can return zero duplicate data — only the symbols that actually changed.
Wire format version is negotiated automatically. You don’t need to configure it unless you are integrating with a custom MCP client that requires a specific version.

Build docs developers (and LLMs) love