SDL-MCP provides a structured, token-efficient protocol for AI coding agents. Without explicit instruction, agents default to native file-read and shell tools — wasting tokens on code they don’t need. This guide explains how to enforce SDL-MCP tool use and how agents should work through the Iris Gate Ladder.
A client can have SDL-MCP connected and still spend tokens on native reads and shell commands. Without enforcement:
- Agents read entire files instead of requesting targeted symbol slices
- Agents shell out to
grep or find instead of calling sdl.symbol.search
- Context windows fill up with irrelevant code before the agent finds what it needs
SDL-MCP’s tool enforcement generates client-specific instruction files and config that redirects agents to SDL tools first.
Run the init command with --enforce-agent-tools for each client you use:
# Claude Code
sdl-mcp init --client claude-code --enforce-agent-tools
# Codex
sdl-mcp init --client codex --enforce-agent-tools
# Gemini
sdl-mcp init --client gemini --enforce-agent-tools
# OpenCode
sdl-mcp init --client opencode --enforce-agent-tools
This enables SDL runtime and exclusive Code Mode, then generates the repo-local instruction files and client-specific enforcement assets for the chosen client.
Paste-Ready Agent Instruction Block
Copy the block below into AGENTS.md (or CLAUDE.md for Claude) at the root of your project. Replace [repoid] with your repo’s actual ID.
This is the canonical instruction block from SDL-MCP’s own docs. It covers all 12 workflow categories an agent needs to use SDL-MCP correctly.
## SDL-MCP Token-Efficient Protocol (v0.10)
- Repository ID: `[repoid]`
- MCP Server: `sdl-mcp`
### 0) Establish state before deep context
1. Call `sdl.repo.status` first.
2. If local code changed and indexing is stale, run `sdl.index.refresh` with `mode: "incremental"`.
3. Call `sdl.policy.get` and honor returned caps. Current effective policy is:
- `maxWindowLines: 180`
- `maxWindowTokens: 1400`
- `requireIdentifiers: true`
### 1) The Iris Gate Ladder (Token-Efficient Context Escalation)
Use this order unless task constraints force escalation:
1. `sdl.repo.overview` (start with `level: "stats"`; use `directories`/`full` only when needed).
2. `sdl.symbol.search` with a tight `limit` (`5-20` to start; default is `50`, max is `1000`).
- Add `semantic: true` to enable embedding-based reranking for fuzzy or conceptual queries.
3. `sdl.symbol.getCard` for single lookups; send `ifNoneMatch` to get `notModified` responses.
- Provide exactly one of `symbolId` or `symbolRef`.
- Use `sdl.symbol.getCards` (batch, up to 100 IDs) when fetching multiple symbols.
- Pass `knownEtags` to `getCards` for delta fetching.
- Use `minCallConfidence` to filter low-confidence call edges from card responses.
4. `sdl.slice.build` with explicit budget and compact output:
- Keep `wireFormat: "compact"` (default) and `wireFormatVersion: 2` (default).
- Set budget early: `{ "maxCards": 30, "maxEstimatedTokens": 4000 }`.
- Use `minConfidence` to drop low-trust edges (default `0.5`).
- Always provide `entrySymbols` when available.
- **Auto-discovery mode**: pass `taskText` (and optionally `stackTrace`, `failingTestPath`, or `editedFiles`) instead of `entrySymbols`.
5. `sdl.slice.refresh` if you already have a `sliceHandle`; prefer refresh over rebuilding.
6. `sdl.slice.spillover.get` only when necessary; keep `pageSize` small (default `20`, max `100`).
7. `sdl.code.getSkeleton` before `hotPath` or raw windows.
8. `sdl.code.getHotPath` with focused identifiers (`1-3` identifiers, low `contextLines`, default `3`).
9. `sdl.code.needWindow` last. Keep requests tight:
- `expectedLines <= 180`
- `maxTokens <= 1400`
- Non-empty `identifiersToFind` (required by policy)
- Pass `sliceContext` to give the gating engine task context.
### 2) Task-specific workflows
- **Debug**: `search -> card -> slice.build -> hotPath -> needWindow (only if still ambiguous)`.
- **Debug (auto-discovery)**: `slice.build` with `taskText` describing the bug + `stackTrace` and/or `failingTestPath`.
- **Feature implementation**: `repo.overview -> search -> card -> slice.build`.
- **PR review**: `delta.get -> pr.risk.analyze -> card/hotPath for high-risk symbols`.
- **Live editing**: `buffer.push` as files change → `buffer.checkpoint` to persist → search/card/slice reflect draft state.
- **Context export**: `context.summary` with `format: "clipboard"` for non-MCP tools.
- **Test execution**: `runtime.execute` with narrowest useful runtime.
- **Multi-step chain** *(Code Mode)*: `sdl.action.search` -> focused `sdl.manual` -> `sdl.chain`.
### 3) Do not
- Do not jump directly to raw file reads if SDL tools can answer the question.
- Do not call `sdl.code.needWindow` before trying `sdl.code.getSkeleton`/`sdl.code.getHotPath`.
- Do not use broad `sdl.symbol.search` limits by default.
- Do not rebuild slices repeatedly when `sdl.slice.refresh` can provide incremental deltas.
- Do not call `sdl.symbol.getCard` N times when `sdl.symbol.getCards` can fetch all N in one call.
- Do not skip `sdl.agent.feedback` after completing a task.
- Do not call `sdl.runtime.execute` without setting `timeoutMs`.
- Do not ignore `nextBestAction`, `fallbackTools`, or `fallbackRationale` in denied responses.
The Iris Gate Ladder
The Iris Gate Ladder is SDL-MCP’s core token-efficiency mechanism. It defines the order in which agents escalate context access — from cheapest to most expensive:
Rung 1 — Symbol search and cards
Start here. Use sdl.symbol.search with a tight limit (5–20 results). Fetch individual cards with sdl.symbol.getCard or batch with sdl.symbol.getCards.Cost: Minimal. Cards contain signatures, dependencies, and LLM summaries without raw code bodies.{
"repoId": "my-repo",
"query": "validateToken",
"limit": 10,
"semantic": true
}
Rung 2 — Graph slice
Build a dependency graph slice from entry symbols. This gives structured context about how symbols relate — far cheaper than reading files.{
"repoId": "my-repo",
"entrySymbols": ["sha256:abc..."],
"budget": { "maxCards": 30, "maxEstimatedTokens": 4000 },
"wireFormat": "compact"
}
Use taskText instead of entrySymbols for auto-discovery mode:{
"repoId": "my-repo",
"taskText": "token validation is failing for expired JWTs",
"stackTrace": "Error: invalid signature\n at validateToken..."
}
Rung 3 — Skeleton and hot path
When a slice isn’t enough, use sdl.code.getSkeleton for deterministic skeleton IR (signatures + control flow, elided bodies), then sdl.code.getHotPath for lines matching specific identifiers.{
"repoId": "my-repo",
"symbolId": "sha256:abc...",
"identifiersToFind": ["errorCode", "retryCount"],
"contextLines": 3
}
Rung 4 — Raw code window (escalate)
Last resort. Requires proof-of-need: a reason, the identifiers you expect to find, and an expected line count. Gated by the policy engine.{
"repoId": "my-repo",
"symbolId": "sha256:abc...",
"reason": "Need to inspect the exact error handling branch for expired tokens",
"identifiersToFind": ["TokenExpiredError", "expiresAt"],
"expectedLines": 60,
"maxTokens": 800,
"sliceContext": {
"taskText": "debug JWT expiration handling"
}
}
Agent Task
│
▼
sdl.repo.status ← Check index freshness
│
▼
sdl.symbol.search ← Locate relevant symbols
│
▼
sdl.symbol.getCard(s) ← Read signatures, deps, summaries
│
▼
sdl.slice.build ← Graph context around entry symbols
│
▼
sdl.code.getSkeleton ← Signatures + control flow (no bodies)
│
▼
sdl.code.getHotPath ← Lines matching specific identifiers
│
▼
sdl.code.needWindow ← Full raw code (gated, last resort)
│
▼
sdl.agent.feedback ← Record useful/missing symbols
Feedback Loop
After completing any task, call sdl.agent.feedback to improve future slice quality:
{
"repoId": "my-repo",
"versionId": "<from sdl.repo.status>",
"sliceHandle": "<from sdl.slice.build>",
"usefulSymbols": ["sha256:abc...", "sha256:def..."],
"missingSymbols": ["sha256:xyz..."],
"taskType": "debug",
"taskText": "fix JWT expiration handling",
"taskTags": ["auth", "jwt"]
}
This trains the slice ranker. Use sdl.agent.feedback.query with limit and since (ISO timestamp) to review aggregated stats on which symbols are most frequently useful or missing.
Context Summary for Non-MCP Contexts
Use sdl.context.summary to generate a token-bounded summary for clipboard, PR descriptions, or tickets:
{
"repoId": "my-repo",
"query": "authentication middleware",
"budget": 2000,
"format": "clipboard",
"scope": "task"
}
scope: "task" — multi-symbol summary
scope: "symbol" — single-symbol summary
format: "clipboard" — paste-ready plain text
format: "markdown" — structured Markdown
format: "json" — structured JSON
SDL-MCP exposes 32 MCP tools across 14 categories:
| Category | Tool | Purpose |
|---|
| Repository | sdl.repo.status | Index health, watcher state, prefetch stats |
| sdl.index.refresh | Full or incremental re-indexing |
| sdl.repo.overview | Token-efficient codebase overview |
| Symbols | sdl.symbol.search | Search by name or summary; supports semantic: true |
| sdl.symbol.getCard | Single symbol card with ETag caching |
| sdl.symbol.getCards | Batch fetch up to 100 cards |
| Slices | sdl.slice.build | Build graph slice from entry symbols or task text |
| sdl.slice.refresh | Incremental delta from an existing slice handle |
| sdl.slice.spillover.get | Paginated fetch for overflow symbols |
| Code Access | sdl.code.getSkeleton | Signatures + control flow, no bodies |
| sdl.code.getHotPath | Lines matching specified identifiers |
| sdl.code.needWindow | Full raw code window (gated) |
| Policy | sdl.policy.get | Read current policy settings |
| sdl.policy.set | Update policy (merge patch) |
| Agent | sdl.agent.orchestrate | Autonomous task execution |
| sdl.agent.feedback | Record useful/missing symbols after a task |
| sdl.agent.feedback.query | Query feedback statistics |
| Context | sdl.context.summary | Token-bounded summary for non-MCP contexts |
| Runtime | sdl.runtime.execute | Sandboxed subprocess execution |
| sdl.runtime.queryOutput | Search stored runtime output artifacts |
| Memory | sdl.memory.store | Store cross-session development memory |
| sdl.memory.query | Search memories by text, type, or tags |
| sdl.memory.surface | Auto-surface relevant memories for a task |
| sdl.memory.remove | Soft-delete a memory |
| Deltas | sdl.delta.get | Delta pack between two versions |
| Risk | sdl.pr.risk.analyze | Analyze PR risk and blast radius |
| Live Buffers | sdl.buffer.push | Push editor buffer for draft-aware indexing |
| sdl.buffer.checkpoint | Persist draft changes into the symbol overlay |
| sdl.buffer.status | Check live buffer state |
| Usage | sdl.usage.stats | Cumulative token usage and savings metrics |
| Diagnostics | sdl.info | Runtime, config, and native-addon status |
| Code Mode | sdl.action.search | Discover relevant SDL actions |
| sdl.manual | Filtered API reference |
| sdl.chain | Multi-step action chain (up to 50 steps) |