A Model Context Protocol (MCP) server that provides powerful code analysis and modification tools for .NET projects using Microsoft's Roslyn compiler platform. Spelunk.NET is available as a .NET global tool for easy installation and use.
Primitive Tools, Intelligent Agents
Spelunk.NET follows a primitive tools philosophy:
- Tools are simple, focused, and composable building blocks
- Complex refactorings are agent workflows that orchestrate these primitives
- This approach provides flexibility, transparency, and adaptability
Important: Refactoring operations (like SQL parameterization, async conversion, etc.) are not implemented as monolithic tools but as agent workflows. See REFACTORING_AS_AGENTS.md for details.
This MCP server exposes Roslyn's advanced code analysis capabilities, allowing AI agents to:
- Analyze C#, VB.NET, and F# code at both syntactic and semantic levels
- Search for symbols, references, and patterns across entire solutions
- Navigate code relationships and AST structure with XPath-style queries
- Modify code safely using Roslyn's syntax tree manipulation
- Refactor with confidence using semantic understanding
- Query abstract syntax trees with enhanced SpelunkPath expressions
The server provides precise statement-level operations, treating statements as the fundamental unit of code modification. This approach:
- Preserves code structure - Maintains proper indentation and formatting
- Handles edge cases - Correctly manages comments, trivia, and block structures
- Enables composability - Complex refactorings built from simple operations
- Provides stability - Operations work reliably across different code styles
The server leverages Roslyn's unique ability to seamlessly move between syntactic representation (how code looks) and semantic representation (what code means). This enables:
- Finding ALL uses of a property, even through method calls and assignments
- Understanding type relationships and inheritance
- Tracking data flow through the codebase
- Making context-aware modifications
spelunk/find-class- Find classes by name with wildcardsspelunk/find-method- Find methods by name with wildcardsspelunk/find-property- Find properties by name with wildcardsspelunk/find-references- Find all references to any symbolspelunk/find-implementations- Find all implementations of an interfacespelunk/find-overrides- Find all overrides of a virtual/abstract methodspelunk/find-derived-types- Find all types that inherit from a base class
spelunk/find-method-calls- Find what methods a given method callsspelunk/find-method-callers- Find what methods call a given method
spelunk/find-statements- Find statements matching patterns with stable IDsspelunk/replace-statement- Replace any statement preciselyspelunk/insert-statement- Insert statements before/after existing onesspelunk/remove-statement- Remove statements while preserving comments
spelunk/query-syntax- Query AST with XPath-style expressionsspelunk/navigate- Navigate from a position using axesspelunk/get-ast- Get AST structure at any levelspelunk/mark-statement- Mark statements with ephemeral markersspelunk/find-marked-statements- Find previously marked statementsspelunk/unmark-statement- Remove specific markersspelunk/clear-markers- Clear all markers
-
spelunk/rename-symbol- Safely rename any symbol with full validation- Prevents renaming system types
- Validates identifiers (including @keyword support)
- Shows impact analysis
- Detects naming conflicts
-
spelunk/edit-code- Perform surgical code editsadd-method- Add methods to classesadd-property- Add properties to classesmake-async- Convert synchronous methods to async
-
spelunk/fix-pattern- Find and fix code patterns across the codebase
spelunk/load-workspace- Load a .NET solution or projectspelunk/analyze-syntax- Get syntax tree informationspelunk/workspace-status- Check workspace loading status
spelunk-fsharp-load-project- Load an F# project (.fsproj)spelunk-fsharp-find-symbols- Find F# symbols with pattern matchingspelunk-fsharp-query- Query F# AST using FSharpPath expressionsspelunk-fsharp-get-ast- Get F# abstract syntax tree structure
-
spelunk/query-syntax- Query AST using enhanced SpelunkPath expressions- Support for expression-level nodes (binary-expression, literal, identifier)
- Advanced predicates (@operator, @literal-value, @contains)
- Find specific patterns like null checks, string comparisons
-
spelunk/navigate- Navigate from any position using XPath-style axes- Navigate to parent, ancestor, child, descendant nodes
- Find siblings with following-sibling:: and preceding-sibling::
- Chain navigation paths like "parent::method/following-sibling::method"
-
spelunk/get-ast- Get abstract syntax tree structure- Visualize code hierarchy and node relationships
- Debug SpelunkPath queries
- Understand AST node types for pattern matching
Install Spelunk.NET as a global tool (recommended):
# Install from NuGet (coming soon)
dotnet tool install --global Spelunk.NET
# Or install from local build
dotnet pack src/Spelunk.Server/Spelunk.Server.csproj
dotnet tool install --global --add-source ./src/Spelunk.Server/nupkg Spelunk.NET- .NET 10.0 SDK (or .NET 8.0 or later)
- MSBuild (comes with .NET SDK)
- OR Docker (if running in container)
Once installed, use the spelunk command:
# Run in stdio mode (for MCP clients like Claude Desktop)
spelunk stdio
# Run SSE server on default port (3333)
spelunk sse
# Run SSE server on custom port
spelunk sse -p 8080
# Check SSE server status
spelunk sse status
# View SSE server logs
spelunk sse logs
spelunk sse logs -f # Follow mode
# Stop SSE server
spelunk sse stop
# Restart SSE server
spelunk sse restartConfigure allowed directories and server behavior in your user config file:
- Unix/Linux/macOS:
~/.spelunk/config.json - Windows:
%USERPROFILE%\.spelunk\config.json
Example for Unix/Linux/macOS:
{
"Spelunk": {
"AllowedPaths": [
"/Users/yourname/Repos",
"/path/to/your/projects"
],
"Logging": {
"MinimumLevel": "Information"
},
"Server": {
"RequestTimeoutSeconds": 120,
"MaxWorkspaces": 10,
"WorkspaceTimeoutMinutes": 15,
"HistoryTimeoutHours": 1,
"MaxMarkers": 100,
"CleanupIntervalMinutes": 10
}
}
}Example for Windows (note: backslashes must be escaped as \\):
{
"Spelunk": {
"AllowedPaths": [
"C:\\Users\\yourname\\Repos",
"C:\\Projects",
"D:\\Code"
],
"Server": {
"WorkspaceTimeoutMinutes": 30,
"MaxMarkers": 200
}
}
}Required:
AllowedPaths- List of directories the server can access
Server Options (all optional with sensible defaults):
RequestTimeoutSeconds- Request timeout (default: 120, range: 1-3600)MaxWorkspaces- Maximum concurrent workspaces (default: 10, range: 1-100)WorkspaceTimeoutMinutes- Idle timeout before unloading (default: 15, range: 1-1440)HistoryTimeoutHours- History retention (default: 1, range: 1-168)MaxMarkers- Maximum ephemeral markers per session (default: 100, range: 1-10000)CleanupIntervalMinutes- Cleanup timer interval (default: 10, range: 1-60)
Logging Options:
MinimumLevel- Log level: Trace, Debug, Information, Warning, Error, Critical (default: Information)
Or use environment variables:
export SPELUNK_ALLOWED_PATHS="/path/to/code:/another/path"
spelunk stdioBuild from source:
dotnet build
dotnet run --project src/Spelunk.Server -- stdioThe server can also run in a Docker container:
# Build the image
docker build -t spelunk:latest .
# Run with mounted code directory
docker run -i \
-v /path/to/your/code:/workspace \
-e SPELUNK_ALLOWED_PATHS=/workspace \
spelunk:latestFor detailed Docker usage, see DOCKER.md.
// Load a workspace
{
"tool": "spelunk/load-workspace",
"arguments": {
"path": "/path/to/MySolution.sln"
}
}
// Find all references to a method
{
"tool": "spelunk/find-references",
"arguments": {
"symbolName": "GetUserAsync",
"symbolType": "method",
"containerName": "UserController"
}
}// Find all Console.WriteLine statements
{
"tool": "spelunk/find-statements",
"arguments": {
"pattern": "Console.WriteLine",
"patternType": "text"
}
}
// Replace a specific statement
{
"tool": "spelunk/replace-statement",
"arguments": {
"location": {
"file": "/path/to/Program.cs",
"line": 25,
"column": 9
},
"newStatement": "_logger.LogInformation(\"Application started\");"
}
}
// Insert validation at the start of a method
{
"tool": "spelunk/insert-statement",
"arguments": {
"position": "before",
"location": {
"file": "/path/to/UserService.cs",
"line": 30,
"column": 9
},
"statement": "ArgumentNullException.ThrowIfNull(user);"
}
}
// Mark statements for multi-step refactoring
{
"tool": "spelunk/mark-statement",
"arguments": {
"location": {
"file": "/path/to/OrderService.cs",
"line": 45,
"column": 13
},
"label": "validation-point"
}
}// Add a method to a class
{
"tool": "spelunk/edit-code",
"arguments": {
"file": "Services/UserService.cs",
"operation": "add-method",
"className": "UserService",
"code": "public async Task<bool> IsValidUser(int id) { return await GetUser(id) != null; }"
}
}
// Fix a pattern across the codebase
{
"tool": "spelunk/fix-pattern",
"arguments": {
"findPattern": "DateTime.Now",
"replacePattern": "DateTime.UtcNow",
"patternType": "property-access"
}
}The server uses:
- Roslyn for all code analysis and manipulation
- MSBuild for loading and understanding project structure
- JSON-RPC 2.0 over stdio for MCP communication
- In-memory workspace management for performance
- Atomic operations with preview support
- All rename operations validate against reserved keywords and system types
- Code modifications go through Roslyn's syntax validation
- Preview mode available for all modifications
- Impact analysis shows affected files before changes
- Semantic validation ensures type safety
The architecture supports advanced scenarios like:
- Pattern-based code transformations
- Cross-file refactoring
- Semantic code search
- Data flow analysis
- Custom code fixes
- Agent Tool Selection Guide - Decision tree for choosing between semantic and syntactic tools
- Agent Query Examples - Concrete examples comparing approaches for common tasks
- Tool Synopsis - Complete reference for all available tools
- Semantic vs Syntactic Philosophy - Architectural philosophy behind tool categories
- Statement-Level Editing - Design principles for code modification
- F# Architecture - Understanding F# support implementation
- SpelunkPath Instructions - Quick reference for query syntax
- SpelunkPath Agent Guide - 5-minute introduction for AI agents
- SpelunkPath Syntax Design - Complete syntax specification
Key areas for enhancement:
- Additional edit-code operations (add-parameter, wrap-try-catch, etc.)
- Generic syntax tree manipulation tools
- Semantic analysis tools (data flow, type relationships)
- Performance optimizations for large solutions