DEV Community

JF Meyers
JF Meyers

Posted on

RoslynLens: Give Your AI Assistant Semantic Eyes on .NET Code

Reading a 1,500-line C# file to find one method signature wastes tokens, time, and money. RoslynLens is an open-source MCP server that gives AI coding assistants like Claude Code direct access to Roslyn semantic analysis — so they can query your .NET codebase the way an IDE does, not the way grep does.

One MCP call. 30-150 tokens. No file reading required.

The problem: AI assistants are blind navigators

When Claude Code (or any AI assistant) works with a .NET codebase, it has two options to understand your code:

  1. Read entire files — 500 to 2,000+ tokens per .cs file, most of which is irrelevant noise
  2. Grep and hope — text-based search that can't distinguish a type name from a variable, a declaration from a usage

Neither approach understands your code. They process text. They miss inheritance hierarchies, cross-project references, interface implementations, and semantic relationships.

The result? Wasted context window, missed connections, and wrong assumptions.

The solution: semantic queries via MCP

RoslynLens loads your .NET solution into a Roslyn MSBuildWorkspace and exposes 28 tools through the Model Context Protocol. Instead of reading files, the AI assistant sends focused queries and gets back structured, minimal responses.

Before RoslynLens — finding who calls OrderService.SubmitAsync:

1. Grep for "SubmitAsync" across 200 files -> 47 matches (including comments, strings, other methods)
2. Read 12 files to narrow down -> ~15,000 tokens consumed
3. Still not sure about indirect calls through interfaces
Enter fullscreen mode Exit fullscreen mode

After RoslynLens — same task:

find_callers("OrderService.SubmitAsync")
-> 4 callers with file, line, containing method  85 tokens
Enter fullscreen mode Exit fullscreen mode

Quick start

Install as a global .NET tool:

dotnet tool install --global RoslynLens
Enter fullscreen mode Exit fullscreen mode

Register with Claude Code:

claude mcp add --scope user --transport stdio roslyn-lens -- roslyn-lens
Enter fullscreen mode Exit fullscreen mode

That's it. RoslynLens auto-discovers the nearest .sln or .slnx file (BFS, max 3 levels), loads it in the background, and starts serving queries over stdio.

You can also configure it via .mcp.json in your project root:

{
  "mcpServers": {
    "roslyn-lens": {
      "type": "stdio",
      "command": "roslyn-lens",
      "args": []
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

28 tools at a glance

RoslynLens tools fall into six categories.

Symbol navigation

Tool What it does
find_symbol Locate definitions by name — supports glob patterns (*Service, Get*User)
find_references All usages of a symbol across the solution
find_implementations Interface implementors and derived classes
find_callers Direct callers of a method
find_overrides Overrides of virtual/abstract methods
find_dead_code Unused types, methods, properties — with filters for public members, entry points, project/file scope

Inspection

Tool What it does
get_public_api Public surface of a type — no file reading
get_symbol_detail Full signature, parameters, return type, XML docs
get_type_hierarchy Inheritance chain, interfaces, derived types
get_project_graph Solution dependency tree (with projectFilter for large solutions)
get_dependency_graph Method call chain visualization
get_diagnostics Compiler warnings and errors
get_test_coverage_map Heuristic test-to-code mapping

Analysis

Tool What it does
get_complexity_metrics Cyclomatic, cognitive complexity, nesting depth, LOC
analyze_data_flow Variable assignments, reads, writes, captured variables
analyze_control_flow Reachability, return points, exit points
detect_antipatterns 18 built-in detectors (see below)
detect_circular_dependencies Project and type-level cycle detection
detect_duplicates Structurally similar code via AST fingerprinting

Compound tools (one call, multiple analyses)

Tool What it does
analyze_method Signature + callers + dependencies + complexity in one call
get_type_overview Public API + hierarchy + implementations + diagnostics
get_file_overview Types + diagnostics + anti-patterns for a file

Batch operations (multiple symbols, one call)

Tool What it does
find_symbols_batch Resolve multiple symbol names at once
get_public_api_batch Public API of multiple types at once
get_symbol_detail_batch Details of multiple symbols at once

External & specialized

Tool What it does
resolve_external_source Resolve NuGet/framework source via SourceLink or decompilation
get_module_depends_on [DependsOn] attribute graph for modular monoliths
validate_conventions Convention violation checker

18 anti-pattern detectors

RoslynLens doesn't just navigate code — it catches common .NET mistakes at query time. Detectors run on syntax trees with optional semantic model analysis, so they're fast and don't require a full compilation for basic checks.

General .NET

ID What it catches
AP001 async void methods (except event handlers)
AP002 Sync-over-async: .Result, .Wait(), .GetAwaiter().GetResult()
AP003 new HttpClient() instead of IHttpClientFactory
AP004 DateTime.Now/UtcNow instead of TimeProvider
AP005 catch (Exception) without re-throw
AP006 String interpolation in log calls (structured logging violation)
AP007 #pragma warning disable without matching restore
AP008 Async methods missing CancellationToken parameter
AP009 EF Core queries without .AsNoTracking()

Domain-specific

ID What it catches
GR-GUID Guid.NewGuid() instead of IGuidGenerator
GR-SECRET Hardcoded passwords, connection strings, API keys
GR-SYNC-EF SaveChanges() instead of SaveChangesAsync()
GR-BADREQ TypedResults.BadRequest<string>() instead of Problem()
GR-REGEX new Regex() instead of [GeneratedRegex]
GR-SLEEP Thread.Sleep() in production code
GR-CONSOLE Console.Write/WriteLine instead of ILogger
GR-CFGAWAIT Missing ConfigureAwait(false) in library code
GR-DTO Classes/records with *Dto suffix (naming convention violation)

Adding a new detector is straightforward. For simple invocation matches (Foo.Bar()), extend InvocationDetectorBase. For new Foo() patterns, extend ObjectCreationDetectorBase. For complex logic, implement IAntiPatternDetector directly.

Deep dive: key v1.1 features

Fuzzy FQN resolution

AI assistants often send imprecise symbol names. Instead of failing, RoslynLens uses a three-tier fallback strategy:

  1. Exact match — case-insensitive, highest priority
  2. Partial namespaceMyService resolves to MyApp.Services.MyService
  3. Levenshtein distance — typo tolerance (1-2 edits depending on name length)

When multiple candidates match, the response includes a disambiguation list with fully qualified names, file paths, and line numbers — so the AI can pick the right one without another round-trip.

Duplicate code detection via AST fingerprinting

Text-based diff tools miss renamed-but-identical logic. RoslynLens normalizes syntax trees by replacing identifiers with ID, literals with LIT, and types with TYPE, then computes a SHA256 fingerprint of the normalized structure. Methods with identical fingerprints are structurally equivalent — regardless of variable names.

detect_duplicates(projectFilter: "MyProject", minStatements: 5)
-> 3 duplicate groups, each with locations + similarity score
Enter fullscreen mode Exit fullscreen mode

Complexity metrics (SonarSource model)

get_complexity_metrics computes four metrics per method:

  • Cyclomatic complexity — counts decision points (if, while, for, switch, catch, &&, ||, ??)
  • Cognitive complexity — SonarSource model with nesting penalties
  • Nesting depth — maximum block nesting level
  • Logical LOC — excluding blanks and comments

Supports method, type, and project scope with a configurable threshold to surface only the worst hotspots.

Data flow and control flow analysis

Built on Roslyn's SemanticModel.AnalyzeDataFlow() and AnalyzeControlFlow():

  • Data flow: variables declared, read inside, written inside, always assigned, captured by closures
  • Control flow: start/end reachability, return statement count, exit points

External source resolution

When the AI needs to understand a NuGet package API, RoslynLens follows a resolution hierarchy:

  1. SourceLink — check if the symbol has source locations in the solution
  2. Decompilation — fallback via ICSharpCode.Decompiler (truncated to 60 lines for token efficiency)

No more telling the AI to "just check the NuGet documentation."

Architecture: designed for large solutions

RoslynLens is built to handle real-world .NET solutions with 50+ projects:

  • Background loading — the solution loads asynchronously via a BackgroundService. Tools return a "loading" status until the workspace is ready, then serve queries instantly.
  • Lazy compilation with LRU cache — solutions with many projects compile on-demand. A configurable LRU cache (default: 20 compilations) keeps frequently accessed projects hot.
  • File watchers.cs changes trigger incremental text updates on the Roslyn workspace. .csproj changes trigger a full reload. No restart needed.
  • Logs to stderr — stdout is reserved for JSON-RPC (MCP protocol). All diagnostic output goes to stderr.
src/RoslynLens/
├── Program.cs                  # Host + MCP stdio transport
├── SolutionDiscovery.cs        # BFS .sln/.slnx auto-discovery
├── WorkspaceManager.cs         # MSBuildWorkspace, LRU compilation cache
├── WorkspaceInitializer.cs     # Background solution loading
├── SymbolResolver.cs           # Cross-project resolution + fuzzy FQN
├── ComplexityAnalyzer.cs       # Cyclomatic/cognitive complexity
├── DuplicateCodeDetector.cs    # AST fingerprinting
├── ExternalSourceResolver.cs   # SourceLink + decompilation
├── Tools/                      # 28 MCP tool implementations
├── Analyzers/                  # 18 anti-pattern detectors
└── Responses/                  # Token-optimized DTOs
Enter fullscreen mode Exit fullscreen mode

Configuration

Four environment variables for runtime tuning:

Variable Default Purpose
ROSLYN_LENS_TIMEOUT_SECONDS 30 Operation timeout
ROSLYN_LENS_MAX_RESULTS 100 Maximum results per query
ROSLYN_LENS_CACHE_SIZE 20 LRU compilation cache size
ROSLYN_LENS_LOG_LEVEL Information Log verbosity

Real-world impact: token savings

Here's a comparison on a 35-project solution (~180,000 lines of C#):

Task Without RoslynLens With RoslynLens Savings
Find all implementations of IRepository Read 8 files (~12,000 tokens) find_implementations (95 tokens) 99%
Understand OrderService API Read full file (1,800 tokens) get_public_api (120 tokens) 93%
Check for anti-patterns in a module Read 15 files (~22,000 tokens) detect_antipatterns (200 tokens) 99%
Analyze complexity hotspots Manual review across projects get_complexity_metrics (150 tokens) N/A (not feasible manually)

The compound tools push this further. analyze_method replaces what would be 4-5 separate tool calls (or 4-5 file reads) with a single query.

Key takeaways

  • RoslynLens replaces file reading with semantic queries — 30-150 tokens instead of 500-2,000+ per file
  • 28 tools cover navigation, analysis, batch operations, and compound queries
  • 18 anti-pattern detectors catch common .NET mistakes at query time
  • Fuzzy FQN resolution handles imprecise AI queries gracefully
  • AST-based duplicate detection finds renamed-but-identical logic
  • Works on large solutions — lazy compilation, LRU cache, background loading

Get started

# Install
dotnet tool install --global RoslynLens

# Register with Claude Code
claude mcp add --scope user --transport stdio roslyn-lens -- roslyn-lens

# Done. Navigate to any .NET project and Claude Code can query it.
Enter fullscreen mode Exit fullscreen mode

GitHub: jfmeyers/roslyn-lens | NuGet: RoslynLens | License: Apache-2.0

Built by JF Meyers. Contributions welcome.

Top comments (0)