DotnetPilot.Mcp.Roslyn
Roslyn-powered MCP server providing semantic C# code intelligence for DotnetPilot. Exposes 11 tools via Model Context Protocol: solution structure, class outlines, method bodies, find references, find implementations, DI analysis, architecture violation checking, and EF Core model introspection.
Ask AI about DotnetPilot.Mcp.Roslyn
Powered by Claude Β· Grounded in docs
I know everything about DotnetPilot.Mcp.Roslyn. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
DotnetPilot.Mcp.Roslyn
Roslyn-powered MCP server that gives AI coding assistants semantic understanding of your C# codebase.
Table of Contents
- What It Does
- How It Works
- Requirements
- Installation
- Usage
- Tools Reference
- Configuration
- Examples
- Troubleshooting
- Development
- Publishing
- Author
- License
What It Does
dnp-roslyn is an MCP (Model Context Protocol) server that loads your .NET solution via Roslyn's MSBuildWorkspace and exposes 11 tools for semantic code analysis. Unlike text-based grep/regex approaches, it understands your code at the compiler level β types, symbols, references, inheritance, and DI registration patterns.
| Category | Tool | What it does |
|---|---|---|
| Workspace | get_solution_structure | Lists all projects with references, frameworks, and document counts |
| Workspace | reload_solution | Invalidates cache and reloads from disk |
| File-level | get_class_outline | Member signatures (no bodies) β fast way to understand a class |
| File-level | get_method_body | Full source of one method β token-efficient alternative to reading whole files |
| Solution-level | find_references | Every usage of a symbol across the entire solution |
| Solution-level | find_implementations | All implementations of an interface or overrides of a virtual method |
| DI | find_di_registrations | Every AddScoped, AddTransient, AddSingleton call |
| DI | find_di_consumers | Every class with constructor injection |
| DI | check_di_completeness | Missing registrations + captive dependency detection |
| Architecture | check_architecture_violations | Clean architecture layer rule enforcement |
| EF Core | get_ef_models | DbContexts, entities, properties, navigations, configuration method |
How It Works
- On startup, dnp-roslyn registers the MSBuild SDK via
MSBuildLocatorand opens your solution with Roslyn'sMSBuildWorkspace - The solution is compiled in-memory β this gives full semantic analysis (type resolution, symbol lookup, cross-project references)
- The workspace is cached after first load β subsequent tool calls are fast
- Communication uses MCP's stdio transport (newline-delimited JSON-RPC over stdin/stdout)
- All logging goes to stderr so it never interferes with the MCP protocol
Requirements
- .NET 10 SDK (or later) β both to run dnp-roslyn and to compile your target solution
- A .NET solution file (
.slnor.slnx) β the entry point for workspace loading
The solution must build successfully with dotnet build β Roslyn loads the same MSBuild projects, so unresolved packages or broken project files will cause load failures.
Installation
As a .NET global tool (recommended)
dotnet tool install -g DotnetPilot.Mcp.Roslyn
Verify:
dnp-roslyn version
# dnp-roslyn 0.3.2
Update to latest
dotnet tool update -g DotnetPilot.Mcp.Roslyn
From source
git clone https://github.com/zdanovichnick/dotnet-pilot-mcp-roslyn.git
cd dotnet-pilot-mcp-roslyn
dotnet build
dotnet pack -o ./nupkg
dotnet tool install -g DotnetPilot.Mcp.Roslyn --add-source ./nupkg
Usage
Standalone
Run from your solution directory β dnp-roslyn auto-detects .slnx (preferred) or .sln files:
# Auto-detect solution in current directory
dnp-roslyn
# Specify solution path explicitly
dnp-roslyn --solution path/to/MyApp.slnx
# Verbose logging (sent to stderr, safe for MCP transport)
dnp-roslyn --verbose
# Check your setup
dnp-roslyn doctor
dnp-roslyn version
The doctor command validates:
- MSBuild SDK is found and registered
- .NET SDK version
- Solution file is found and loadable
- All projects load successfully (lists each with file count)
$ dnp-roslyn doctor
dnp-roslyn doctor
=================
MSBuild: .NET SDK 10.0.100 (C:\Program Files\dotnet\sdk\10.0.100)
.NET SDK: 10.0.0
Solution: /home/user/projects/MyApp/MyApp.slnx
Projects: 5
- MyApp.Api (24 files)
- MyApp.Application (18 files)
- MyApp.Domain (12 files)
- MyApp.Infrastructure (15 files)
- MyApp.Tests (20 files)
All checks passed.
With DotnetPilot Plugin
When you install the DotnetPilot Claude Code plugin, the Roslyn server starts automatically. The plugin's .mcp.json is preconfigured:
{
"mcpServers": {
"roslyn": {
"command": "dnp-roslyn",
"args": [],
"env": {
"DNP_ROSLYN_LOG_LEVEL": "Warning"
}
}
}
}
Claude Code launches dnp-roslyn when the plugin activates. The server auto-detects the solution in your current working directory. No manual startup needed.
With Any MCP Client
dnp-roslyn speaks standard MCP over stdio β it works with any MCP-compatible client, not just Claude Code.
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"roslyn": {
"command": "dnp-roslyn",
"args": ["--solution", "path/to/MyApp.slnx"]
}
}
}
Claude Code (standalone, without plugin)
Add to your project's .mcp.json:
{
"mcpServers": {
"roslyn": {
"command": "dnp-roslyn",
"args": [],
"env": {
"DNP_ROSLYN_LOG_LEVEL": "Warning"
}
}
}
}
Other MCP clients
Any client that supports MCP stdio transport can use dnp-roslyn. Start the process with dnp-roslyn --solution <path> and communicate via stdin/stdout using newline-delimited JSON-RPC.
Tools Reference
Workspace Tools
get_solution_structure
Returns the full solution structure as JSON.
Parameters: none
Returns:
{
"solutionPath": "/home/user/projects/MyApp/MyApp.slnx",
"projects": [
{
"name": "MyApp.Domain",
"path": "src/MyApp.Domain/MyApp.Domain.csproj",
"outputKind": "library",
"targetFramework": "net10.0+",
"documentCount": 12,
"projectReferences": []
},
{
"name": "MyApp.Application",
"path": "src/MyApp.Application/MyApp.Application.csproj",
"outputKind": "library",
"targetFramework": "net10.0+",
"documentCount": 18,
"projectReferences": ["MyApp.Domain"]
},
{
"name": "MyApp.Api",
"path": "src/MyApp.Api/MyApp.Api.csproj",
"outputKind": "exe",
"targetFramework": "net10.0+",
"documentCount": 24,
"projectReferences": ["MyApp.Application", "MyApp.Infrastructure"]
}
]
}
reload_solution
Invalidates the cached workspace and reloads from disk. Call this after adding/removing projects, changing project references, or making structural .csproj changes.
Parameters: none
Returns: "Solution reloaded. 5 projects loaded."
File-Level Tools
get_class_outline
Returns member signatures (no method bodies) for classes in a file. Much more token-efficient than reading the entire file when you need to understand a class's API.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
filePath | string | yes | Relative path from solution root (e.g. src/MyApp.Api/Services/UserService.cs) |
className | string | yes | Class name to filter. Pass empty string "" for all classes in the file |
Returns:
[
{
"name": "UserService",
"kind": "ClassDeclaration",
"baseType": "object",
"interfaces": ["IUserService"],
"line": 8,
"members": [
{
"kind": "Method",
"name": "GetByIdAsync",
"signature": "Task<UserDto?> UserService.GetByIdAsync(Guid id)",
"accessibility": "Public",
"isStatic": false,
"line": 18
},
{
"kind": "Constructor",
"name": ".ctor",
"signature": "UserService.UserService(IUserRepository repo, ILogger<UserService> logger)",
"accessibility": "Public",
"isStatic": false,
"line": 12
}
]
}
]
get_method_body
Returns the full source of a specific method, constructor, or property. More token-efficient than reading an entire file when you need one member.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
filePath | string | yes | Relative path from solution root |
methodName | string | yes | Method, constructor, or property name |
Returns:
// src/MyApp.Application/Services/UserService.cs:18
public async Task<UserDto?> GetByIdAsync(Guid id)
{
var user = await _repo.GetByIdAsync(id);
if (user is null) return null;
return new UserDto(user.Id, user.Name, user.Email);
}
Solution-Level Tools
find_references
Finds every usage of a symbol across the entire solution. Works for classes, interfaces, methods, properties, enums, constructors, events, and delegates.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
filePath | string | yes | File where the symbol is defined |
symbolName | string | yes | Name of the symbol |
Returns:
{
"symbol": "IUserService",
"kind": "NamedType",
"referenceCount": 4,
"references": [
{
"file": "src/MyApp.Application/Services/UserService.cs",
"line": 8,
"column": 35,
"code": "public sealed class UserService : IUserService"
},
{
"file": "src/MyApp.Api/Controllers/UsersController.cs",
"line": 12,
"column": 22,
"code": "private readonly IUserService _userService;"
},
{
"file": "src/MyApp.Api/Extensions/ServiceCollectionExtensions.cs",
"line": 15,
"column": 30,
"code": "services.AddScoped<IUserService, UserService>();"
}
]
}
find_implementations
Finds all implementations of an interface, derived classes of a base class, or overrides of a virtual/abstract method.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
filePath | string | yes | File where the interface/base type is defined |
symbolName | string | yes | Interface, abstract class, or virtual method name |
Returns:
{
"symbol": "IUserRepository",
"kind": "NamedType",
"implementationCount": 2,
"implementations": [
{
"name": "UserRepository",
"kind": "NamedType",
"containingType": "",
"file": "src/MyApp.Infrastructure/Repositories/UserRepository.cs",
"line": 6
},
{
"name": "InMemoryUserRepository",
"kind": "NamedType",
"containingType": "",
"file": "tests/MyApp.Tests/Fakes/InMemoryUserRepository.cs",
"line": 5
}
]
}
DI Analysis Tools
find_di_registrations
Finds all dependency injection service registrations across the solution β AddScoped, AddTransient, AddSingleton, AddDbContext, AddHttpClient, and related methods.
Parameters: none
Returns: Array of registrations, each with interface type, implementation type, lifetime, registration method, file, and line number.
find_di_consumers
Finds all classes with constructor injection β any constructor parameter that is an interface or abstract class.
Parameters: none
Returns: Array of consumers, each with the class name, file, line, and list of injected parameter types.
check_di_completeness
Cross-references registrations with consumers to find:
- Missing registrations β a type is injected but never registered
- Captive dependencies β a Scoped service injected into a Singleton (shorter-lived service captured by longer-lived one)
Parameters: none
Returns:
{
"registeredServices": 12,
"consumers": 8,
"missingRegistrations": [
{
"consumer": "OrderService",
"missingType": "IPaymentGateway",
"file": "src/MyApp.Application/Services/OrderService.cs",
"line": 10
}
],
"captiveDependencies": [
{
"singleton": "CacheService",
"captive": "ApplicationDbContext",
"captiveLifetime": "Scoped"
}
]
}
Architecture Tools
check_architecture_violations
Analyzes project references against clean architecture rules and reports violations.
Layer classification:
- Domain β projects with
Domain,Core,Contracts,Shared,BuildingBlocksin the name - Application β projects with
Application,UseCasesin the name - Infrastructure β projects with
Infrastructure,Persistence,Data,Gatewayin the name - Api β projects with
Api,Web,Host,Serverin the name - Tests β projects with
Test,Tests,Specsin the name
Rules enforced:
| Layer | Allowed references |
|---|---|
| Domain | (none β no outward dependencies) |
| Application | Domain only |
| Infrastructure | Domain, Application |
| Api | Application, Infrastructure |
| Tests | (all) |
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
style | string | yes | Architecture style. Currently clean is supported. Pass empty string for default. |
Returns:
{
"style": "clean",
"layers": [
{ "project": "MyApp.Domain", "layer": "Domain" },
{ "project": "MyApp.Application", "layer": "Application" },
{ "project": "MyApp.Infrastructure", "layer": "Infrastructure" },
{ "project": "MyApp.Api", "layer": "Api" }
],
"violations": [
{
"project": "MyApp.Domain",
"layer": "Domain",
"referencedProject": "MyApp.Infrastructure",
"referencedLayer": "Infrastructure",
"severity": "error",
"message": "Domain should not reference Infrastructure"
}
]
}
EF Core Tools
get_ef_models
Discovers all EF Core DbContext subclasses and their entity models. Supports DbContext, IdentityDbContext, and ApiAuthorizationDbContext base types.
Parameters: none
Returns:
{
"contexts": [
{
"name": "ApplicationDbContext",
"project": "MyApp.Infrastructure",
"file": "src/MyApp.Infrastructure/Data/ApplicationDbContext.cs",
"line": 8,
"entities": [
{
"name": "User",
"configuredVia": "fluent",
"properties": [
{ "name": "Id", "type": "Guid", "isKey": true },
{ "name": "Name", "type": "string", "isKey": false },
{ "name": "Email", "type": "string", "isKey": false }
],
"navigations": [
{ "name": "Orders", "targetType": "Order", "isCollection": true }
]
}
]
}
]
}
Configuration detection:
fluentβ entity configured viaOnModelCreating(Entity<T>()calls) orIEntityTypeConfiguration<T>implementationsdata-annotationsβ entity has[Key],[Required],[Table], etc. attributesconventionβ no explicit configuration found
Configuration
Environment variables
| Variable | Default | Description |
|---|---|---|
DNP_ROSLYN_LOG_LEVEL | Warning | Minimum log level: Trace, Debug, Information, Warning, Error, Critical |
CLI flags
| Flag | Description |
|---|---|
--solution <path> | Explicit solution file path (skips auto-detection) |
--verbose | Enable debug logging to stderr |
Auto-detection behavior
When no --solution is specified, dnp-roslyn searches the current directory and walks up parent directories looking for:
*.slnxfiles (modern format, preferred)*.slnfiles (legacy format, fallback)
The first match wins. If no solution is found, the server exits with an error.
Workspace caching
The solution is loaded once on first tool call and cached in memory. Subsequent calls reuse the cached compilation. Call reload_solution to force a fresh load after structural changes (adding projects, changing references, etc.).
File-level and method-level tools do NOT require a reload for content changes β Roslyn reads the file from disk on each call.
Examples
Example 1: Understand a class before modifying it
Instead of reading the entire 300-line file:
Tool: get_class_outline
filePath: "src/MyApp.Application/Services/OrderService.cs"
className: ""
Get a compact overview of all members, then drill into the specific method:
Tool: get_method_body
filePath: "src/MyApp.Application/Services/OrderService.cs"
methodName: "CalculateTotal"
Example 2: Safe refactoring β check all usages first
Before renaming or modifying IUserRepository:
Tool: find_references
filePath: "src/MyApp.Application/Interfaces/IUserRepository.cs"
symbolName: "IUserRepository"
See every file and line that references it across the solution.
Example 3: Verify DI wiring after adding a service
After creating a new service:
Tool: check_di_completeness
Immediately shows if the new service is missing its DI registration.
Example 4: Audit architecture before a PR
Tool: check_architecture_violations
style: "clean"
Catches violations like Domain referencing Infrastructure before code review.
Example 5: Understand the data model
Tool: get_ef_models
See all DbContexts, entities, their properties, navigation relationships, and how each entity is configured.
Troubleshooting
"No MSBuild instance found"
The .NET SDK is not installed or not on PATH. Install it:
# Windows
winget install Microsoft.DotNet.SDK.10
# macOS
brew install dotnet-sdk
# Linux
sudo apt-get install dotnet-sdk-10.0
"No .sln or .slnx file found"
Run dnp-roslyn from a directory containing a solution file, or specify it explicitly:
dnp-roslyn --solution path/to/MyApp.slnx
"Failed to load solution" / project load errors
Your solution must build cleanly with dotnet build first. Common issues:
- Missing NuGet packages β run
dotnet restore - Broken project references β check
.csprojfiles - SDK version mismatch β verify
global.jsonmatches your installed SDK
Run the doctor command for diagnostics:
dnp-roslyn doctor --solution path/to/Your.slnx
Tools return empty results
find_references/find_implementationsreturn nothing: The symbol name must match exactly (case-sensitive). Useget_class_outlinefirst to see exact member names.get_ef_modelsreturns no contexts: Ensure your DbContext class inherits fromDbContext,IdentityDbContext, orApiAuthorizationDbContext. The context must be in a project that's part of the solution.check_di_completenessmisses registrations: OnlyAdd*<T>method-call patterns are detected. Custom extension methods that wrap DI registration may not be recognized.
MCP transport issues
dnp-roslyn uses newline-delimited JSON-RPC over stdin/stdout (not HTTP Content-Length framing). All logs go to stderr. If you see JSON parse errors in your MCP client:
- Ensure you're not redirecting stderr to stdout
- Check that no other process is writing to the same stdout stream
- Use
--verboseto see detailed logs on stderr for debugging
Slow first response
The first tool call loads and compiles the entire solution β this can take 10-30 seconds for large solutions. Subsequent calls use the cached compilation and are fast (typically <1 second). dnp-roslyn eagerly starts loading the workspace on startup to minimize this delay.
Development
Build
cd dotnet-pilot-mcp-roslyn
dotnet build
The repository pins the SDK with global.json, so CI and local builds use the same .NET 10 feature band.
Run unit tests
dotnet test
Tests cover file-level tools, architecture analysis (layer classification for Domain, Application, Infrastructure, Api, Tests, Persistence, Web, Shared, Contracts, BuildingBlocks, Gateway), and EF Core analysis.
Run smoke test
The end-to-end smoke test exercises all 11 tools via MCP JSON-RPC over stdio:
node tests/smoke-test.js --solution path/to/Your.slnx
Requires dnp-roslyn to be installed as a global tool and a valid .NET solution.
Project structure
src/DotnetPilot.Mcp.Roslyn/
Program.cs # Entry point, MSBuildLocator, arg parsing, doctor command
Workspace/
WorkspaceCache.cs # MSBuildWorkspace loader with caching + reload
WorkspaceOptions.cs # Solution path config
Tools/
Workspace/
GetSolutionStructureTool.cs # Solution-level project enumeration
ReloadSolutionTool.cs # Cache invalidation
FileLevel/
GetClassOutlineTool.cs # Class member signatures + FindDocument helper
GetMethodBodyTool.cs # Method/constructor/property source extraction
SolutionLevel/
FindReferencesTool.cs # Cross-solution SymbolFinder.FindReferences
FindImplementationsTool.cs # Interface/abstract implementation finder
Dotnet/
DiAnalyzer.cs # DI registration + consumer analysis engine
DiTools.cs # MCP wrappers for DI tools
ArchitectureAnalyzer.cs # Layer classification + rule enforcement
ArchitectureTools.cs # MCP wrapper for architecture tool
EfCoreAnalyzer.cs # DbContext/entity introspection engine
EfCoreTools.cs # MCP wrapper for EF Core tool
Models/
SolutionModels.cs # SolutionStructure, ProjectInfo
DiModels.cs # DiRegistration, DiConsumer, DiCompletenessReport
ArchitectureModels.cs # ArchitectureReport, LayerInfo, ArchitectureViolation
EfCoreModels.cs # EfCoreReport, DbContextInfo, EntityInfo, etc.
tests/
DotnetPilot.Mcp.Roslyn.Tests/ # xUnit unit tests
smoke-test.js # End-to-end MCP protocol test
Tech stack
| Component | Version | Purpose |
|---|---|---|
| .NET | 10.0 | Runtime and target framework |
| ModelContextProtocol | 1.2.0 | MCP server SDK (stdio transport, tool discovery) |
| Microsoft.CodeAnalysis.CSharp.Workspaces | 5.3.0 | Roslyn C# workspace + semantic analysis |
| Microsoft.CodeAnalysis.Workspaces.MSBuild | 5.3.0 | MSBuild project/solution loader |
| Microsoft.Build.Locator | 1.11.2 | Runtime MSBuild SDK resolution |
Publishing
Local pack validation
Before pushing a release, validate the package locally:
dotnet restore tests/DotnetPilot.Mcp.Roslyn.Tests/DotnetPilot.Mcp.Roslyn.Tests.csproj
dotnet test tests/DotnetPilot.Mcp.Roslyn.Tests/DotnetPilot.Mcp.Roslyn.Tests.csproj -c Release
dotnet pack src/DotnetPilot.Mcp.Roslyn/DotnetPilot.Mcp.Roslyn.csproj -c Release --no-build
Packages are written to artifacts/packages/:
DotnetPilot.Mcp.Roslyn.<version>.nupkgDotnetPilot.Mcp.Roslyn.<version>.snupkg
GitHub Actions publish flow
The repository includes a release workflow at .github/workflows/publish-nuget.yml.
- Add a repository secret named
NUGET_API_KEY - Update the version in
src/DotnetPilot.Mcp.Roslyn/DotnetPilot.Mcp.Roslyn.csproj - Create and push a tag such as
v0.3.3
The workflow restores, builds, tests, packs, uploads the package artifacts, and pushes both the .nupkg and .snupkg files to https://api.nuget.org/v3/index.json.
Author
Nick Zdanovych β zdanovichnick@gmail.com
License
MIT
