Imported from reason-machines/mcp-skills (
skills/sdl-mcp-symbol-delta-ledger/SKILL.md). Install upstream withnpx skills add reason-machines/mcp-skills --skill sdl-mcp-symbol-delta-ledger. Copyright stays with the author.
SDL-MCP: Symbol Delta Ledger
Skill by ara.so — MCP Skills collection.
SDL-MCP is a policy-centered context budget layer for coding agents that transforms sprawling codebases into compact, high-signal context. It indexes code into a searchable symbol graph, providing 4-20x token savings by serving precisely the right amount of context through the Iris Gate Ladder escalation system.
What SDL-MCP Does
- Symbol Cards: Every function, class, interface, type, and variable becomes a ~100 token metadata record instead of ~2,000 tokens of raw code
- Graph Slicing: Follow dependency graphs (not directory boundaries) to get the N most relevant symbols within a token budget
- Iris Gate Ladder: Four-rung context escalation from compact cards to full source (policy-gated)
- Delta Packs & Blast Radius: Semantic change intelligence showing what changes mean and who's affected
- Live Indexing: Real-time code intelligence reflecting unsaved editor changes
- Task-Shaped Retrieval: Context engine that selects the right rungs and evidence for debug/review/implement/explain tasks
Installation
Prerequisites: Node.js 24+ required
Global Installation
# Install globally
npm install -g sdl-mcp
# Non-interactive setup with auto-indexing
sdl-mcp init -y --auto-index
# Start MCP server
sdl-mcp serve --stdio
Using npx
# Quick start with wrapper
npx create-sdl-mcp
# Or direct npx usage
npx --yes sdl-mcp@latest init -y --auto-index
npx --yes sdl-mcp@latest serve --stdio
MCP Client Configuration
Add to your MCP client configuration (e.g., Claude Desktop):
{
"mcpServers": {
"sdl-mcp": {
"command": "sdl-mcp",
"args": ["serve", "--stdio"]
}
}
}
Or with npx:
{
"mcpServers": {
"sdl-mcp": {
"command": "npx",
"args": ["--yes", "sdl-mcp@latest", "serve", "--stdio"]
}
}
}
Key Commands (CLI)
# Initialize repository
sdl-mcp init # Interactive setup
sdl-mcp init -y --auto-index # Non-interactive with auto-index
sdl-mcp init --config-only # Generate config without indexing
# Indexing
sdl-mcp index # Full index
sdl-mcp index --incremental # Only changed files
sdl-mcp index --force # Force re-index all files
# Server
sdl-mcp serve --stdio # Start MCP server
sdl-mcp serve --stdio --verbose # Verbose logging
# Health & Diagnostics
sdl-mcp health # Run health checks
sdl-mcp summary --task "debug auth" # Generate portable context summary
# Configuration
sdl-mcp config get # Show current config
sdl-mcp config set key=value # Update config value
MCP Tools API
Symbol Cards & Search
Get Symbol Card (Rung 1: ~100 tokens)
// Request minimal symbol metadata
{
"name": "sdl.symbol.card",
"arguments": {
"symbolId": "src/auth/validate.ts::validateToken",
"format": "compact" // or "full"
}
}
Search Symbols
{
"name": "sdl.symbol.search",
"arguments": {
"query": "authenticate user",
"limit": 10,
"confidence": 0.7,
"filters": {
"kind": ["function", "class"],
"filePath": "src/auth/**"
}
}
}
Graph Slicing
Build Token-Budgeted Slice
{
"name": "sdl.slice.build",
"arguments": {
"entrySymbols": ["src/auth/validate.ts::validateToken"],
"tokenBudget": 800,
"direction": "both", // "upstream" | "downstream" | "both"
"weights": {
"call": 1.0,
"config": 0.8,
"import": 0.6
}
}
}
Auto-Discovery from Task
{
"name": "sdl.slice.build",
"arguments": {
"taskText": "Fix authentication timeout issue",
"tokenBudget": 1000,
"autoDiscover": true
}
}
Refresh Slice (Delta Updates)
{
"name": "sdl.slice.refresh",
"arguments": {
"sliceHandle": "slice_abc123",
"deltaOnly": true // Only return changed symbols
}
}
Iris Gate Ladder Escalation
Rung 2: Skeleton (signature + structure)
{
"name": "sdl.iris.skeleton",
"arguments": {
"symbolId": "src/auth/AuthService.ts::AuthService",
"includeSignatures": true,
"includeTypes": true
}
}
Rung 3: Hot Path (specific lines/blocks)
{
"name": "sdl.iris.hotPath",
"arguments": {
"symbolId": "src/auth/validate.ts::validateToken",
"targetIdentifiers": ["cache", "decrypt"],
"contextLines": 3
}
}
Rung 4: Raw Source (policy-gated)
{
"name": "sdl.iris.rawSource",
"arguments": {
"symbolId": "src/auth/validate.ts::validateToken",
"reason": "Need to see complete error handling logic for timeout bug",
"expectedIdentifiers": ["TimeoutError", "retry", "backoff"],
"expectedLineCount": 45,
"bypassPolicy": false
}
}
Task-Shaped Context
Get Context for Task
{
"name": "sdl.context",
"arguments": {
"task": "debug", // "debug" | "review" | "implement" | "explain"
"description": "User authentication failing after token refresh",
"tokenBudget": 2000,
"focusPaths": ["src/auth/**"],
"options": {
"semantic": true, // Use hybrid retrieval
"includeTests": true
}
}
}
Delta & Blast Radius
Semantic Diff
{
"name": "sdl.delta.pack",
"arguments": {
"baseRef": "main",
"headRef": "HEAD",
"options": {
"includeBlastRadius": true,
"maxBlastDepth": 3
}
}
}
PR Risk Analysis
{
"name": "sdl.pr.risk.analyze",
"arguments": {
"baseRef": "main",
"headRef": "feature/auth-refactor",
"options": {
"includeFanInTrend": true,
"testRecommendations": true
}
}
}
Live Indexing
Push Buffer Changes
{
"name": "sdl.buffer.push",
"arguments": {
"filePath": "src/auth/validate.ts",
"content": "export async function validateToken(token: string): Promise<User> {\n // ...\n}",
"version": 42
}
}
Clear Buffer
{
"name": "sdl.buffer.clear",
"arguments": {
"filePath": "src/auth/validate.ts"
}
}
Runtime Execution (Sandboxed)
Execute Code
{
"name": "sdl.runtime.execute",
"arguments": {
"executable": "npm",
"args": ["test", "auth.test.ts"],
"cwd": "./",
"outputMode": "minimal", // "minimal" | "summary" | "intent"
"timeout": 30000
}
}
Query Execution Output
{
"name": "sdl.runtime.queryOutput",
"arguments": {
"executionId": "exec_xyz789",
"query": "Show me the failing test assertions"
}
}
Feedback Loop
Record Symbol Usefulness
{
"name": "sdl.agent.feedback",
"arguments": {
"contextId": "ctx_abc123",
"useful": ["src/auth/validate.ts::validateToken"],
"missing": ["src/auth/TokenCache.ts::get"],
"irrelevant": ["src/utils/logger.ts::debug"]
}
}
Configuration
SDL-MCP stores configuration in .sdl-mcp/config.json:
{
"repoRoot": "/path/to/repo",
"languages": {
"typescript": { "enabled": true },
"python": { "enabled": true },
"go": { "enabled": true }
},
"indexing": {
"exclude": ["node_modules/**", "dist/**", "*.test.ts"],
"maxFileSize": 1048576,
"parallelism": 4
},
"governance": {
"rawSourceGate": {
"enabled": true,
"requireReason": true,
"requireIdentifiers": true,
"maxLineCount": 500
},
"runtime": {
"enabled": true,
"allowedExecutables": ["npm", "node", "python3"],
"cwdJail": true,
"timeout": 30000
}
},
"codeMode": {
"exclusive": true,
"autoSlicing": true,
"defaultBudget": 1500
},
"retrieval": {
"hybridThreshold": 0.7,
"semanticDefault": "auto"
}
}
Update Configuration
# Set specific values
sdl-mcp config set governance.rawSourceGate.maxLineCount=1000
sdl-mcp config set codeMode.defaultBudget=2000
# Enable/disable features
sdl-mcp config set governance.runtime.enabled=false
sdl-mcp config set codeMode.exclusive=false
Common Patterns
Pattern 1: Efficient Code Understanding
Instead of reading entire files:
// ❌ Old way: Read whole file (~2000 tokens)
const fileContent = await readFile('src/auth/validate.ts');
// ✅ SDL-MCP way: Get symbol card (~100 tokens)
const card = await mcp.call('sdl.symbol.card', {
symbolId: 'src/auth/validate.ts::validateToken',
format: 'compact'
});
// Returns: signature, parameters, return type, dependencies, ~100 tokens
Pattern 2: Dependency-Aware Context
Instead of directory-based context:
// ❌ Old way: Read all files in directory (~16,000 tokens)
const authFiles = await glob('src/auth/**/*.ts');
// ✅ SDL-MCP way: Graph slice within budget (~800 tokens)
const slice = await mcp.call('sdl.slice.build', {
taskText: 'Fix token validation timeout',
tokenBudget: 800,
autoDiscover: true
});
// Returns: Only relevant symbols following dependency graph
Pattern 3: Controlled Escalation
Only read raw code when necessary:
// Start with card (Rung 1)
const card = await mcp.call('sdl.symbol.card', {
symbolId: 'src/auth/validate.ts::validateToken'
});
// If needed, escalate to skeleton (Rung 2)
const skeleton = await mcp.call('sdl.iris.skeleton', {
symbolId: 'src/auth/validate.ts::validateToken',
includeSignatures: true
});
// If still needed, hot path (Rung 3)
const hotPath = await mcp.call('sdl.iris.hotPath', {
symbolId: 'src/auth/validate.ts::validateToken',
targetIdentifiers: ['cache', 'decrypt'],
contextLines: 3
});
// Only as last resort, raw source (Rung 4, policy-gated)
const raw = await mcp.call('sdl.iris.rawSource', {
symbolId: 'src/auth/validate.ts::validateToken',
reason: 'Need complete error handling for timeout analysis',
expectedIdentifiers: ['TimeoutError', 'retry'],
expectedLineCount: 45
});
Pattern 4: Task-Shaped Retrieval
Let SDL-MCP plan the context:
// Debugging task
const debugContext = await mcp.call('sdl.context', {
task: 'debug',
description: 'Authentication timeout after 30 seconds',
tokenBudget: 2000,
focusPaths: ['src/auth/**'],
options: { includeTests: true }
});
// Code review task
const reviewContext = await mcp.call('sdl.context', {
task: 'review',
description: 'Refactor token validation to use Redis cache',
tokenBudget: 1500,
options: { semantic: true }
});
// Implementation task
const implContext = await mcp.call('sdl.context', {
task: 'implement',
description: 'Add rate limiting to login endpoint',
tokenBudget: 2500,
focusSymbols: ['src/auth/login.ts::handleLogin']
});
Pattern 5: Change Impact Analysis
Understand semantic impact of changes:
// Get semantic diff
const deltaPack = await mcp.call('sdl.delta.pack', {
baseRef: 'main',
headRef: 'feature/cache-refactor',
options: {
includeBlastRadius: true,
maxBlastDepth: 3
}
});
// Analyze PR risk
const riskAnalysis = await mcp.call('sdl.pr.risk.analyze', {
baseRef: 'main',
headRef: 'feature/cache-refactor',
options: {
includeFanInTrend: true,
testRecommendations: true
}
});
// Results include:
// - Semantic changes (not just line diffs)
// - Ranked blast radius (who's affected)
// - Fan-in trends (amplifier symbols)
// - Test re-run recommendations
Pattern 6: Live Development Flow
Work with unsaved changes:
// Push buffer as you type
await mcp.call('sdl.buffer.push', {
filePath: 'src/auth/validate.ts',
content: currentEditorContent,
version: editorVersion
});
// Search reflects live changes
const liveSearch = await mcp.call('sdl.symbol.search', {
query: 'validateToken',
limit: 5
});
// Returns results including unsaved buffer changes
// Build slice with live state
const liveSlice = await mcp.call('sdl.slice.build', {
taskText: 'Review my current auth changes',
tokenBudget: 1000
});
// Clear buffer when done
await mcp.call('sdl.buffer.clear', {
filePath: 'src/auth/validate.ts'
});
Pattern 7: Feedback-Driven Improvement
Help SDL-MCP learn what's useful:
// After using context
await mcp.call('sdl.agent.feedback', {
contextId: 'ctx_abc123',
useful: [
'src/auth/validate.ts::validateToken',
'src/cache/TokenCache.ts::get'
],
missing: [
'src/auth/errors.ts::TimeoutError'
],
irrelevant: [
'src/utils/logger.ts::debug'
]
});
// Future slices will prioritize useful symbols and include missing ones
Real-World Example: Debugging Authentication Issue
// 1. Start with task-shaped context
const context = await mcp.call('sdl.context', {
task: 'debug',
description: 'Users getting 401 errors after token refresh',
tokenBudget: 2000,
focusPaths: ['src/auth/**'],
options: { includeTests: true }
});
// 2. Search for relevant symbols
const symbols = await mcp.call('sdl.symbol.search', {
query: 'token refresh validate',
limit: 10,
confidence: 0.7
});
// 3. Build dependency slice from top candidates
const slice = await mcp.call('sdl.slice.build', {
entrySymbols: symbols.results.slice(0, 3).map(r => r.symbolId),
tokenBudget: 800,
direction: 'both'
});
// 4. Get skeleton of key symbol
const skeleton = await mcp.call('sdl.iris.skeleton', {
symbolId: 'src/auth/TokenService.ts::refreshToken',
includeSignatures: true
});
// 5. If needed, hot path for specific logic
const hotPath = await mcp.call('sdl.iris.hotPath', {
symbolId: 'src/auth/TokenService.ts::refreshToken',
targetIdentifiers: ['verify', 'expire', 'blacklist'],
contextLines: 5
});
// 6. Run tests to verify hypothesis
const testRun = await mcp.call('sdl.runtime.execute', {
executable: 'npm',
args: ['test', 'auth/token.test.ts'],
outputMode: 'summary',
timeout: 30000
});
// 7. Provide feedback
await mcp.call('sdl.agent.feedback', {
contextId: context.id,
useful: [
'src/auth/TokenService.ts::refreshToken',
'src/auth/TokenService.ts::verifyToken'
],
missing: [
'src/auth/blacklist.ts::isBlacklisted'
]
});
Token Comparison:
- Reading all auth files: ~16,000 tokens
- SDL-MCP approach: ~1,500 tokens
- Savings: 10.6x
Troubleshooting
Installation Issues
Peer dependency warnings:
# Suppress harmless ERESOLVE warnings
npm install -g sdl-mcp --legacy-peer-deps
Tree-sitter grammar issues:
# Rebuild grammars
npm rebuild tree-sitter tree-sitter-kotlin
Node version mismatch:
# Check version (must be 24+)
node -v
# Install Node 24+ from https://nodejs.org
Indexing Issues
Index fails on large repo:
# Increase file size limit
sdl-mcp config set indexing.maxFileSize=2097152
# Reduce parallelism if memory-constrained
sdl-mcp config set indexing.parallelism=2
Files not indexed:
# Check exclusion patterns
sdl-mcp config get indexing.exclude
# Force re-index
sdl-mcp index --force
Language not detected:
# Enable language explicitly
sdl-mcp config set languages.python.enabled=true
# Re-run auto-index
sdl-mcp init --auto-index
Query Issues
Low search quality:
// Enable semantic search
{
"name": "sdl.symbol.search",
"arguments": {
"query": "authentication logic",
"options": { "semantic": true }
}
}
// Or update default
sdl-mcp config set retrieval.semanticDefault=true
Slice too small/large:
// Adjust token budget
{
"name": "sdl.slice.build",
"arguments": {
"entrySymbols": ["..."],
"tokenBudget": 1500 // Increase from default 800
}
}
Raw source denied:
// Check policy requirements
sdl-mcp config get governance.rawSourceGate
// Provide required justification
{
"name": "sdl.iris.rawSource",
"arguments": {
"symbolId": "...",
"reason": "Specific reason why card/skeleton/hotPath insufficient",
"expectedIdentifiers": ["specific", "names", "needed"],
"expectedLineCount": 45 // Within maxLineCount limit
}
}
Runtime Issues
Execution denied:
# Check allowed executables
sdl-mcp config get governance.runtime.allowedExecutables
# Add executable to allowlist
sdl-mcp config set governance.runtime.allowedExecutables='["npm","node","python3","go"]'
Execution timeout:
// Increase timeout
{
"name": "sdl.runtime.execute",
"arguments": {
"executable": "npm",
"args": ["test"],
"timeout": 60000 // 60 seconds
}
}
Health Checks
# Run full health check
sdl-mcp health
# Common issues reported:
# - Database corruption: Run `sdl-mcp index --force`
# - Missing grammars: Run `npm rebuild tree-sitter*`
# - Config errors: Run `sdl-mcp config get` to inspect
# - Index stale: Run `sdl-mcp index --incremental`
Environment Variables
# Database location (default: .sdl-mcp/db)
export SDL_DB_PATH=/custom/path/to/db
# Log level
export SDL_LOG_LEVEL=debug # error | warn | info | debug
# Disable telemetry
export SDL_TELEMETRY=false
# Max workers for indexing
export SDL_MAX_WORKERS=8
Performance Tips
- Use incremental indexing during development:
sdl-mcp index --incremental - Set appropriate token budgets (800-1500 typical, 2000+ for complex tasks)
- Use focusPaths/focusSymbols to scope searches when possible
- Enable deltaOnly on slice refreshes to minimize re-fetching
- Leverage live indexing instead of re-indexing after every edit
- Provide feedback to improve future slice quality
- Use Code Mode (
codeMode.exclusive: true) for most efficient agent workflow