Agent skill

mcp-narsil

Deep code intelligence via Narsil MCP providing 76 tools for security scanning (OWASP, CWE, taint analysis), call graph analysis (CFG, DFG, callers/callees), structural queries (symbols, definitions, references), and supply chain security (SBOM, license compliance). Accessed via Code Mode for token efficiency.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/development/mcp-narsil

SKILL.md

Narsil MCP - Deep Code Intelligence

Rust-powered code intelligence providing 76 specialized tools for security scanning, call graph analysis, and structural queries. Accessed via Code Mode for token-efficient on-demand access (~700 tokens vs ~6,000-8,000 native).

Core Principle: Unified code intelligence - Narsil handles STRUCTURE, SECURITY, and SEMANTIC search.


1. 🎯 WHEN TO USE

Activation Triggers

Use when:

  • Security vulnerability scanning needed (OWASP, CWE, injections)
  • Call graph or control flow analysis required
  • Finding symbols, definitions, or references
  • Dead code detection or complexity analysis
  • SBOM generation or license compliance
  • Git blame, history, or hotspot analysis

Keyword Triggers:

  • Security: "security scan", "vulnerability", "injection", "OWASP", "CWE", "taint"
  • Call Graph: "call graph", "callers", "callees", "who calls", "what calls"
  • Structure: "find symbols", "find functions", "project structure", "references"
  • Quality: "dead code", "unreachable", "complexity", "unused"
  • Supply Chain: "SBOM", "dependencies", "licenses", "CVE"
  • Git: "git blame", "hotspots", "contributors", "history"

Use Cases

Security Auditing

  • Full security scan with OWASP Top 10 and CWE Top 25
  • SQL injection, XSS, command injection detection
  • Taint analysis tracing untrusted data flow
  • Secret and credential detection

Code Understanding

  • Project structure overview
  • Symbol search (functions, classes, structs)
  • Call graph visualization
  • Dependency analysis

Code Quality

  • Dead code and unreachable code detection
  • Complexity metrics (cyclomatic, cognitive)
  • Unused assignments and uninitialized variables

Supply Chain Security

  • SBOM generation (CycloneDX, SPDX)
  • CVE checking against OSV database
  • License compliance verification

When NOT to Use

Do not use for:

  • Simple text pattern search → Use Grep
  • File path/name matching → Use Glob

For semantic search: Use narsil_neural_search - Narsil's neural backend handles meaning-based queries.


2. 🧭 SMART ROUTING

Activation Detection

TASK CONTEXT
    │
    ├─► Security scanning needed
    │   └─► Load: references/security_guide.md
    │       └─► Tools: scan_security, find_injection_vulnerabilities, trace_taint
    │
    ├─► Call graph / code flow analysis
    │   └─► Load: references/call_graph_guide.md
    │       └─► Tools: get_call_graph, get_callers, get_callees
    │
    ├─► Quick overview / first use
    │   └─► Load: references/quick_start.md
    │       └─► Tools: get_project_structure, find_symbols
    │
    ├─► Full tool reference needed
    │   └─► Load: references/tool_reference.md
    │       └─► All 76 tools documented
    │
    └─► Simple structural query
        └─► Use SKILL.md only
            └─► Tools: find_symbols, get_project_structure

Resource Router

python
def route_narsil_resources(task):
    """
    Resource Router for mcp-narsil skill
    Load references based on task context
    """

    # ──────────────────────────────────────────────────────────────────
    # SECURITY SCANNING
    # Purpose: OWASP, CWE, injection detection, taint analysis
    # Key Insight: Start with scan_security for overview, drill into specifics
    # ──────────────────────────────────────────────────────────────────
    if task.involves_security or task.mentions_vulnerability:
        return load("references/security_guide.md")  # Security workflow

    # ──────────────────────────────────────────────────────────────────
    # CALL GRAPH ANALYSIS
    # Purpose: CFG, DFG, callers/callees, complexity metrics
    # Key Insight: Start with get_call_graph, then drill with callers/callees
    # ──────────────────────────────────────────────────────────────────
    if task.involves_call_graph or task.mentions_code_flow:
        return load("references/call_graph_guide.md")  # Analysis workflow

    # ──────────────────────────────────────────────────────────────────
    # QUICK START
    # Purpose: First-time usage, verification, basic commands
    # Key Insight: Fastest path to working state
    # ──────────────────────────────────────────────────────────────────
    if task.is_first_use or task.needs_verification:
        return load("references/quick_start.md")  # Getting started

    # ──────────────────────────────────────────────────────────────────
    # COMPLETE REFERENCE
    # Purpose: All 76 tools with descriptions and priority
    # Key Insight: Use for discovery or when unsure which tool
    # ──────────────────────────────────────────────────────────────────
    if task.needs_tool_discovery or task.needs_full_reference:
        return load("references/tool_reference.md")  # All tools

    # Default: SKILL.md covers basic structural queries

# ══════════════════════════════════════════════════════════════════════
# STATIC RESOURCES (always available, not conditionally loaded)
# ══════════════════════════════════════════════════════════════════════
# assets/tool_categories.md → Priority categorization of all 76 tools
# scripts/update-narsil.sh   → Script to update Narsil binary

3. 🛠️ HOW IT WORKS

Code Mode Invocation

Narsil is accessed via Code Mode's call_tool_chain() for token efficiency.

Naming Convention:

narsil.narsil_{tool_name}

Process Flow:

STEP 1: Discover Tools
       ├─ Use search_tools() for capability-based discovery
       ├─ Use tool_info() for specific tool details
       └─ Output: Tool name and parameters
       ↓
STEP 2: Execute via Code Mode
       ├─ Use call_tool_chain() with TypeScript code
       ├─ Await narsil.narsil_{tool_name}({params})
       └─ Output: Tool results
       ↓
STEP 3: Process Results
       └─ Parse and present findings

Tool Invocation Examples

Important: Most Narsil tools require a repo parameter. Use list_repos() to discover the repo name (typically "unknown" if not explicitly named).

typescript
// Discover security tools
search_tools({ task_description: "security vulnerability scanning" });

// Get tool details
tool_info({ tool_name: "narsil.narsil_scan_security" });

// First: Discover repo name
call_tool_chain({
  code: `
    const repos = await narsil.narsil_list_repos({});
    return repos;  // Returns repo name (e.g., "unknown")
  `
});

// Execute security scan (repo required)
call_tool_chain({
  code: `
    const findings = await narsil.narsil_scan_security({
      repo: "unknown"
    });
    return findings;
  `
});

// Find all functions (repo required, use symbol_type not kind)
call_tool_chain({
  code: `
    const symbols = await narsil.narsil_find_symbols({
      repo: "unknown",
      symbol_type: "function"
    });
    return symbols;
  `
});

// Get call graph (repo required, use function not function_name)
call_tool_chain({
  code: `
    const graph = await narsil.narsil_get_call_graph({
      repo: "unknown",
      function: "main"
    });
    return graph;
  `
});

// Get symbol definition (repo required, use symbol not name)
call_tool_chain({
  code: `
    const def = await narsil.narsil_get_symbol_definition({
      repo: "unknown",
      symbol: "myFunction"
    });
    return def;
  `
});

Unified Code Search

Narsil provides both structural AND semantic search capabilities:

Query Type Tool Reason
"How does authentication work?" narsil_neural_search Semantic meaning
"Find code similar to this" narsil_neural_search Vector similarity
"List all auth functions" narsil_find_symbols Structural query
"Scan for vulnerabilities" narsil_scan_security Security analysis
"Show call graph for login" narsil_get_call_graph Code flow

Neural Semantic Search

Narsil supports three neural embedding backends for semantic code search:

Backend Model API Key Dimensions Best For
Voyage AI voyage-code-2 VOYAGE_API_KEY 1536 Code search (RECOMMENDED)
OpenAI text-embedding-3-small OPENAI_API_KEY 1536 General purpose
Local ONNX Built-in None required 384 Offline/privacy

Configuration Examples:

bash
# Voyage AI (recommended for code)
--neural --neural-backend api --neural-model voyage-code-2
# Requires: VOYAGE_API_KEY in .env

# OpenAI
--neural --neural-backend api --neural-model text-embedding-3-small
# Requires: OPENAI_API_KEY in .env

# Local ONNX (no API key needed)
--neural --neural-backend onnx
# No API key required - runs locally

Tool: narsil_neural_search Fallback: BM25 search if neural unavailable

typescript
// Example: Semantic code search
call_tool_chain({
  code: `
    const results = await narsil.narsil_neural_search({ 
      query: "how does authentication work",
      top_k: 10 
    });
    return results;
  `
});

See references/tool_reference.md for complete tool documentation.

Error Handling Patterns

Narsil tools can fail for various reasons. Use these patterns for robust error handling.

Pattern 1: Check Index Status Before Search

typescript
call_tool_chain({
  code: `
    const status = await narsil.narsil_get_index_status({});
    
    if (!status.neural_ready || status.embedding_count === 0) {
      console.log("Neural index building, using structural query");
      const symbols = await narsil.narsil_find_symbols({
        repo: "unknown",
        symbol_type: "function",
        name_pattern: "auth"
      });
      return { fallback: true, results: symbols };
    }
    
    const results = await narsil.narsil_neural_search({
      query: "authentication flow"
    });
    return { fallback: false, results };
  `
});

Pattern 2: Try-Catch with Retry

typescript
call_tool_chain({
  code: `
    try {
      const findings = await narsil.narsil_scan_security({ repo: "unknown" });
      return { success: true, findings };
    } catch (error) {
      if (error.message?.includes("not found")) {
        await narsil.narsil_reindex({});
        const findings = await narsil.narsil_scan_security({ repo: "unknown" });
        return { success: true, retried: true, findings };
      }
      throw error;
    }
  `,
  timeout: 60000
});

Pattern 3: Graceful Degradation

typescript
call_tool_chain({
  code: `
    async function robustSearch(query) {
      try {
        const neural = await narsil.narsil_neural_search({ query, top_k: 10 });
        if (neural.results?.length > 0) return { method: "neural", results: neural.results };
      } catch (e) { console.log("Neural unavailable:", e.message); }
      
      try {
        const semantic = await narsil.narsil_semantic_search({ repo: "unknown", query });
        if (semantic.results?.length > 0) return { method: "semantic", results: semantic.results };
      } catch (e) { console.log("Semantic unavailable:", e.message); }
      
      const symbols = await narsil.narsil_find_symbols({
        repo: "unknown",
        name_pattern: query.split(" ")[0]
      });
      return { method: "symbols", results: symbols };
    }
    
    return await robustSearch("authentication handler");
  `
});

4. 📋 RULES

✅ ALWAYS

  1. ALWAYS use Code Mode for Narsil invocation

    • Call via call_tool_chain() with TypeScript
    • Saves ~5,300 tokens vs native MCP
  2. ALWAYS use full tool naming convention

    • Format: narsil.narsil_{tool_name}
    • Example: narsil.narsil_scan_security({})
  3. ALWAYS use neural_search for semantic queries

    • "How does X work?" → narsil_neural_search
    • "Find code like X" → narsil_neural_search
  4. ALWAYS load security_guide.md for security tasks

    • Provides phased workflow with checkpoints
    • Ensures comprehensive coverage
  5. ALWAYS verify tool exists before calling

    • Use search_tools() or tool_info() first
    • Prevents "tool not found" errors

❌ NEVER

  1. NEVER skip the narsil_ prefix in tool names

    • Wrong: await narsil.scan_security({})
    • Right: await narsil.narsil_scan_security({})
  2. NEVER use Narsil's LSP/remote tools unnecessarily

    • LSP: IDE handles this natively
    • Remote: Not needed for local development
  3. NEVER assume index is current

    • Use narsil_reindex({}) after file changes
    • Index persists with --persist flag

⚠️ ESCALATE IF

  1. ESCALATE IF large repository causes timeout

    • Increase timeout: { timeout: 120000 }
    • Use --persist flag to save index
    • Consider indexing subset of files
  2. ESCALATE IF tool returns unexpected results

    • Verify tool name with tool_info()
    • Check if repository is indexed with list_repos()
    • Trigger reindex() if needed
  3. ESCALATE IF security findings seem incomplete

    • Verify all categories enabled in scan
    • Check language support (15 languages)
    • Consider taint analysis for deeper inspection

⚠️ KNOWN LIMITATIONS

  1. JavaScript Call Graph

    • Call graph analysis may return empty or incomplete results for JavaScript
    • tree-sitter-javascript has limited support for dynamic call patterns
    • Workaround: Use find_symbols + get_symbol_definition for JS code analysis
  2. Security Scanning Language Support

    • Security rules are primarily designed for backend languages (Rust, Python, Go, Java, C/C++)
    • JavaScript/TypeScript security coverage is limited
    • Frontend-specific vulnerabilities (DOM XSS) may not be fully detected
  3. Neural Search After Index Clear

    • Clearing .narsil-index/ requires OpenCode restart to rebuild neural embeddings
    • Simply calling reindex() may not regenerate embeddings if MCP server has stale state
    • Fix: Exit OpenCode (Ctrl+C), then restart
  4. Git Blame Unicode (Fixed in local build)

    • Original Narsil had issues with files containing Unicode box-drawing characters
    • Local build includes fix for char-safe string slicing
  5. Code Mode Process Spawning

    • Code Mode spawns fresh Narsil MCP processes for each call batch
    • Search indexes (BM25, TF-IDF, Neural) are in-memory and rebuilt on each spawn
    • Impact: Semantic search may return empty if index hasn't built yet (~40-60s)
    • Workaround 1: Keep related calls in same call_tool_chain() batch
    • Workaround 2 (Recommended): Use HTTP server mode for reliable search (see below)

HTTP Server Workaround for Search

For reliable search functionality, use Narsil's HTTP server instead of Code Mode:

bash
# Start HTTP server (indexes build once and stay warm)
./.opencode/skill/mcp-narsil/scripts/narsil-server.sh start

# Wait ~60s for indexes, then search reliably
./.opencode/skill/mcp-narsil/scripts/narsil-search.sh semantic "query"
./.opencode/skill/mcp-narsil/scripts/narsil-search.sh neural "how does X work"
./.opencode/skill/mcp-narsil/scripts/narsil-search.sh symbols function

# Or call any tool via HTTP
curl -X POST http://localhost:3001/tools/call \
  -H "Content-Type: application/json" \
  -d '{"tool": "semantic_search", "args": {"repo": "unknown", "query": "test"}}'

Scripts available:

  • narsil-server.sh - Server management (start/stop/restart/status/logs)
  • narsil-search.sh - Search CLI (neural/semantic/code/hybrid/symbols)
  1. Functions with Limited Results

    • get_project_structure: May return empty tree structure
    • find_references: Returns 0 for most symbols (needs call graph traversal)
    • get_chunk_stats: Returns 0 (aggregate stats not populated)
    • hybrid_search: Requires BOTH BM25 + Neural indexes to be built
  2. Code Mode Config Loading (CRITICAL)

    • Code Mode loads .utcp_config.json at startup only
    • Changes to Narsil config require OpenCode restart to take effect
    • If narsil is not defined error: restart OpenCode
    • Verify after restart: code_mode_list_tools() should show narsil.* tools
    • Common config issues: extra fields, relative paths, missing transport: "stdio"

Function Categories by Index Dependency

Category Functions Timing
AST-Based (work immediately) find_symbols, get_symbol_definition, get_file, get_chunks, find_dead_code, scan_security, get_recent_changes Instant
Index-Based (need ~40-60s) semantic_search, search_code, hybrid_search, neural_search, find_similar_code 40-60s after server start

Tip: Use AST-based functions for immediate results. For semantic search, either wait ~60s after first Narsil call or trigger reindex() and wait.


5. 🏆 SUCCESS CRITERIA

Security Audit Complete

Security audit complete when:

  • scan_security executed with OWASP/CWE rules
  • find_injection_vulnerabilities checked SQL/XSS/command
  • trace_taint analyzed untrusted data flow
  • generate_sbom created dependency manifest
  • check_dependencies verified against CVE database
  • check_licenses confirmed compliance
  • ✅ All critical/high findings addressed or documented

Code Analysis Complete

Code analysis complete when:

  • get_project_structure provided overview
  • find_symbols identified key components
  • get_call_graph mapped function relationships
  • find_dead_code identified cleanup candidates
  • get_complexity flagged refactoring targets
  • narsil_neural_search used for semantic understanding

Validation Checkpoints

Checkpoint Validation
tools_discovered search_tools() returns Narsil tools
repo_indexed list_repos() shows repository
scan_complete Security scan has zero unaddressed criticals
analysis_complete All structural queries executed

6. 🔌 INTEGRATION POINTS

Framework Integration

This skill operates within the behavioral framework defined in AGENTS.md.

Key integrations:

  • Gate 2: Skill routing via skill_advisor.py
  • Tool Routing: Per AGENTS.md Section 6 decision tree
  • Memory: Context preserved via Spec Kit Memory MCP

Code Mode Integration

Configuration: Add to .utcp_config.json:

CRITICAL:

  • Use absolute path for command (e.g., /Users/username/bin/narsil-mcp)
  • Do NOT include extra fields like _note, _neural_backends in mcpServers
  • Config changes require OpenCode restart to take effect
json
{
  "name": "narsil",
  "call_template_type": "mcp",
  "config": {
    "mcpServers": {
      "narsil": {
        "transport": "stdio",
        "command": "/absolute/path/to/narsil-mcp",
        "args": [
          "--repos", ".",
          "--index-path", ".narsil-index",
          "--git",
          "--call-graph",
          "--persist",
          "--watch",
          "--neural",
          "--neural-backend", "api",
          "--neural-model", "voyage-code-2"
        ],
        "env": {
          "VOYAGE_API_KEY": "${VOYAGE_API_KEY}"
        }
      }
    }
  }
}

Finding your Narsil path:

bash
which narsil-mcp
# Use the output as the absolute path in config

After config changes:

  1. Save .utcp_config.json
  2. Restart OpenCode (Ctrl+C, then restart)
  3. Verify: code_mode_list_tools() should show narsil.* tools

Note: The --http flag enables a visualization web UI at localhost:3000, NOT HTTP transport for MCP. MCP transport is always stdio.

Index Persistence

Narsil supports persisting the code index to disk for faster startup:

Flag Purpose Default
--persist Enable saving/loading index from disk Disabled
--index-path Custom index storage location ~/.cache/narsil-mcp/

Project-local indexes (recommended): Use --index-path .narsil-index and add .narsil-index/ to .gitignore.

Manual save: Use narsil.narsil_save_index({}) via Code Mode to trigger index save.

HTTP Server & Visualization

Narsil includes an HTTP server with React frontend for interactive graph visualization.

Important: The --http flag enables a visualization web UI, NOT HTTP transport for MCP. MCP communication is always via stdio - Code Mode spawns the Narsil process and communicates via stdin/stdout. The HTTP server runs in parallel for visual exploration.

Starting the HTTP Server (CRITICAL):

The HTTP server requires stdin to stay open. Use this pattern to keep it running:

bash
# Backend (port 3000) - MUST use stdin pipe to prevent EOF shutdown
(tail -f /dev/null | narsil-mcp \
  --repos . \
  --index-path .narsil-index \
  --git --call-graph --persist \
  --http --http-port 3000 > /tmp/narsil-http.log 2>&1) &

# Frontend (port 5173) - in separate terminal
cd /path/to/narsil-mcp/frontend && npm install && npm run dev

Why the pipe? MCP servers read from stdin. Without input, they receive EOF and shut down. The tail -f /dev/null | keeps stdin open indefinitely.

Graph Views & Limitations:

View Purpose Required Parameters
import Module import/export None (best for JS)
call Function call graph None
symbol Symbol definitions Requires root parameter (function name)
hybrid Combined import + call Requires repo parameter
flow Data flow None

Known Frontend Issues:

  • Symbol view shows "Symbol view requires a root" - enter a function name in the Root field
  • Hybrid view may fail without repo parameter - use Import or Call view instead

Performance: Use Limit slider in UI, index specific dirs with -r src -r lib, add **/node_modules to .gitignore.

Related Skills

Skill Integration
mcp-code-mode Tool orchestration - Narsil accessed via Code Mode's call_tool_chain()
system-spec-kit Context preservation - save security findings for future sessions

Tool Usage Guidelines

Bash: Execute update script, verify binary Read: Load reference files for detailed workflows

Binary Location

The Narsil MCP server source is embedded in the skill folder:

.opencode/skill/mcp-narsil/mcp_server/

Binary path (after build):

.opencode/skill/mcp-narsil/mcp_server/target/release/narsil-mcp

The absolute path is configured in .utcp_config.json. A symlink at ~/bin/narsil-mcp also points to this binary.

To update Narsil:

bash
bash .opencode/skill/mcp-narsil/scripts/update-narsil.sh

7. 🏎️ QUICK REFERENCE

Essential Commands

Task Tool Example
Semantic search neural_search narsil.narsil_neural_search({ query: "authentication flow" })
Find symbols find_symbols narsil.narsil_find_symbols({ symbol_type: "function" })
Security scan scan_security narsil.narsil_scan_security({ repo: "." })
Call graph get_call_graph narsil.narsil_get_call_graph({ function: "main" })
Find callers get_callers narsil.narsil_get_callers({ function: "login" })
Find references find_references narsil.narsil_find_references({ symbol: "UserService" })

Common Patterns

typescript
// Semantic code search
call_tool_chain({
  code: `
    const results = await narsil.narsil_neural_search({
      query: "how does authentication work"
    });
    return results;
  `
});

// Security scan
call_tool_chain({
  code: `
    const findings = await narsil.narsil_scan_security({
      repo: "."
    });
    return findings;
  `
});

// Find all functions
call_tool_chain({
  code: `
    const functions = await narsil.narsil_find_symbols({
      symbol_type: "function"
    });
    return functions;
  `
});

Troubleshooting

Issue Solution
Neural search returns empty Wait 60s after server start for index to build
Call graph empty for JS Known limitation - use find_symbols + find_references instead
Tool not found Check naming: narsil.narsil_{tool}
Slow first query Index building in progress - check with get_index_status

Error Quick Reference

Error Pattern Cause Solution
"not found" in message Repo not indexed Run narsil.narsil_reindex({})
"timeout" in message Large codebase Use categories filter, increase timeout
neural_ready: false Index building Wait ~60s or use AST-based tools
Empty results No matches or index issue Try different search method

8. 🔗 RELATED RESOURCES

scripts/

Script Purpose Usage
update-narsil.sh Update to latest version bash .opencode/skill/mcp-narsil/scripts/update-narsil.sh
narsil-server.sh HTTP server management bash scripts/narsil-server.sh start|stop|status
narsil-search.sh Search CLI bash scripts/narsil-search.sh neural "query"

references/

Document Purpose Key Insight
tool_reference.md All 76 tools documented Complete parameter reference
security_guide.md Security scanning workflow OWASP/CWE checkpoints
call_graph_guide.md Call graph analysis CFG/DFG patterns
quick_start.md Getting started 5-minute setup

assets/

Asset Purpose
tool_categories.md Priority categorization of all 76 tools

External Resources

Related Skills

Install Guide

Didn't find tool you were looking for?

Be as detailed as possible for better results