Skip to content
MBrekhofPublic

About

C# code intelligence MCP server. Wraps Roslyn MSBuildWorkspace; serves 13 symbol-level navigation tools (find_references, goto_definition, find_implementations, semantic_search, etc.) over stdio to AI coding agents.

Resources

Stars

5 stars

Watchers

1 watching

Forks

Repository files navigation

mcpRoslyn

mcpRoslyn system overview rendered by Archify.Net: Claude Code talks over MCP stdio to mcpRoslyn.exe (MCP stdio host, 20 tool handlers, WorkspaceService, MSBuildWorkspace, SymbolIndex, and InvocationIndex), reading solution and source files, running project analyzers, and optionally writing rename edits to the working tree.

Open interactive diagram ↗ · Rendered from docs/images/mcproslyn.architecture.json by archify finalize architecture (Archify.Net).

MCP server exposing C# symbol-level navigation (find-references, goto-definition, find-implementations, semantic-search, rename, etc.) to AI coding agents. Wraps Roslyn's MSBuildWorkspace and serves over stdio.

Status: v1.4, tagged v1.4.0. 20 tools plus echo, 239 tests. Measured timings are in docs/acceptance/, the v1 design in docs/plans/2026-05-15-mcproslyn-design.md, the architecture summary in ARCHITECTURE.md, and open work on the ContextBoard project mcpRoslyn (board-only since 2026-10-03; list_cards).

Latest sessions (2026-09-22, 2026-09-23, 2026-10-20)

  • Repository review & bug-fix sweep (2026-10-20): find_dead_code_candidates no longer flags explicit interface implementations or private helpers they reach (TOOL-015); find_callers seeds the target symbol into seen and deduplicates multiple call sites in one caller body (TOOL-016); reload_workspace checks MSBuildFailure before touching the workspace (WS-010); find_callees walks both halves of partial methods (TOOL-017); SymbolIndex.Refresh expands the dirty document walk when a file newly joins an existing partial symbol (IDX-006); analyze_symbol and find_derived_types deduplicate multi-reference and partial-declaration results (TOOL-018); removed sync-over-async in ComputeConfidence and build warning in AcceptanceTests (TOOL-019).
  • The server no longer dies at startup when global.json pins an uninstalled SDK, no solution is found, or --solution is wrong: it stays up, every tool answers WORKSPACE_NOT_LOADED naming the cause, and reload_workspace recovers (WS-008).
  • Orphaned mcpRoslyn.exe processes after Codex reviews traced to the Codex plugin's broker lifecycle, not this server; closed without code (WS-009).

Earlier changes are in CHANGELOG.md, and per-session detail in SESSION_HANDOFF.md.

Why

AI coding agents (Claude Code, Cursor, etc.) typically navigate codebases with text search (grep, ripgrep). For C# this loses precision: text search can't distinguish a usage from a definition, can't find implementations of an interface, can't resolve overloads, and can't follow renames. mcpRoslyn gives agents authoritative semantic queries backed by the same engine the C# compiler uses.

Requirements

  • Windows. v1 is Windows-only — MSBuildLocator and path-comparison code would need work for Linux/Mac.
  • .NET 10 SDK (for build) — runtime is bundled into the published self-contained exe.
  • A Claude Code installation (or any MCP-compatible client speaking stdio JSON-RPC).

Tools (20, plus echo)

Category Tools
Navigation find_references, goto_definition, workspace_symbol, hover
Structure find_implementations, find_derived_types, list_document_symbols
Callers / Callees find_callers, find_callees
Diagnostics get_compilation_errors, get_document_diagnostics
Search semantic_search (patterns: derives-from:, implements:, has-attribute:, returns:, parameter-type:)
Composite analyze_symbol (hover + refs + impls + derived + callers in one call)
Architecture project_overview, find_entrypoints, find_registrations
Tests test_map (production → test heuristic)
Cleanup find_dead_code_candidates
Editing rename_symbol (preview by default; applyEdits: true to write)
Lifecycle reload_workspace
Sanity echo

Every tool returns structured JSON and accepts format = "structured" | "summary". Navigation tools accept either { filePath, line, column } (cursor style) or { symbolId } (Roslyn's DocumentationCommentId format). rename_symbol is the only path to file writes — default applyEdits: false returns a preview.

Finding dead code

find_dead_code_candidates reports private/internal members with no references. On an application solution (a web app plus tests — nothing packaged), pass includePublicTypes: true:

{ "includePublicTypes": true, "includeTests": false }

Public members are never candidates — outside callers can reach them in ways a reference scan can't see. Public types in an application are a different matter: nothing outside the solution can reach them, so an unreferenced one is usually dead, and it is the case worth acting on. Left off by default because it is exactly the wrong behaviour for a packable library, where the public surface is the product.

Findings are filtered against the things a reference scan can't see on its own: DI registrations (including hosted services and unrecognised registration calls), framework-reached types (controllers, hubs, middleware, EF migrations), and compiler-generated members. Public-type findings are reported at medium confidence — a type resolved only by assembly scanning (Scrutor) or named in configuration has no code reference anywhere, so confirm before deleting.

Workspace diagnostics

reload_workspace and project_overview return WorkspaceLoadDiagnostic { Kind, Message, ProjectName }. Kind distinguishes three situations that MSBuild reports through one channel:

Kind Meaning
Failure The project genuinely did not load.
ProjectLoadedWithWarnings It loaded fine; MSBuild grumbled about package pruning, a vulnerability advisory, etc.
SkippedUnsupportedProject Not a C#/VB project (.esproj, .sqlproj, …). Expected in a polyglot solution.

Stale workspace warnings

Edits to files already in the solution are picked up on every call. What needs reload_workspace — a .cs file added or deleted, a .csproj, .sln or Directory.Build.props changed — is detected and reported instead: every tool result then carries a warnings entry naming what changed, e.g. The workspace may be stale: TestLib\AddedAfterLoad.cs added. Answers may miss these changes; call reload_workspace to load them. An answer with that warning may be incomplete (a find_references blind to callers in the new file); reload before trusting it. mcpRoslyn does not reload on its own — a reload costs seconds of warm-up.

Wiring into Claude Code

mcpRoslyn supports two modes:

Global (user-level ~/.claude/mcp.json) — recommended for multi-project work

Register once:

{
  "mcpServers": {
    "mcpRoslyn": {
      "command": "c:\\projects\\mcpRoslyn\\bin\\publish\\mcpRoslyn.exe"
    }
  }
}

mcpRoslyn discovers the solution by first walking up from Claude Code's CWD looking for *.sln or *.slnx (the closest enclosing solution wins). If none is found up the tree, it then searches down (breadth-first, skipping bin/obj/node_modules/packages and dot-directories) so a solution nested in a subfolder like src/ is still found — the shallowest match wins. When one directory holds several (a lean and a full solution side by side), the one declaring the most C#/VB projects wins, then .sln before .slnx, then name. project_overview shows which solution is loaded; to switch mid-session, call reload_workspace with solutionPath set to the full path of another .sln/.slnx.

Per-project (.mcp.json in the project root) — pin to one solution

Useful when a project contains multiple .sln files and you want to force a specific one:

{
  "mcpServers": {
    "mcpRoslyn": {
      "command": "c:\\projects\\mcpRoslyn\\bin\\publish\\mcpRoslyn.exe",
      "args": ["--solution", "c:\\path\\to\\specific.sln"]
    }
  }
}

CLI arguments

Flag Required Description
--solution <path> No Path to a .sln or .slnx. When omitted, mcpRoslyn walks up from CWD, then searches down into subfolders, looking for one.
--log-level <level> No Debug, Information (default), Warning, Error.
--log-file <path> No Tee ILogger output to a file (append mode). Useful because Claude Code only surfaces MCP stderr during the initialize handler — anything after that (warm-up timings, per-tool diagnostics) is otherwise lost.

Building

dotnet publish c:\projects\mcpRoslyn\src\mcpRoslyn -c Release -o c:\projects\mcpRoslyn\bin\publish

Produces mcpRoslyn.exe as a single-file self-contained win-x64 binary at the publish path. The BuildHost-netcore\ and BuildHost-net472\ directories alongside the exe are required — they contain the Roslyn MSBuild host process that the workspace loader spawns separately at runtime.

Performance

On duetGPT.sln (4 loaded projects, 598 .cs files), measured cold-start and query times:

Metric v1 v1.1 v1.2
Solution load (LoadAsync return) ~8.5 s ~2 s ~2–11 s (env variance; same code path)
Warm-up + index build (background) n/a ~8 s ~23 s
First find_references ~8.4 s ~1.9 s ~640 ms
find_implementations ~300 ms ~830 ms ~320 ms
semantic_search has-attribute: ~11 s ~7.7 s ~11 ms (1000× faster)

All warm-up cost stays in the background — LoadAsync returns at the same time. Full detail in docs/acceptance/.

Index build costs, re-measured 2026-09-13 (published exe cb88412, cold process; BPG over 3 runs, duetGPT over 2):

Solution Projects Warm-up SymbolIndex InvocationIndex
BPG.sln 8 ~2.5 s 91–125 ms ~925 ms
duetGPT.sln 4 ~10.9 s 258–387 ms ~6 400 ms

On 2026-08-15 InvocationIndex measured 1 715 ms on BPG and ~12 200 ms on duetGPT. Raw timings on this machine swing up to ~2× with no code change, and BPG has since lost ~1 700 lines of dead code, so read the drop as a trend rather than a result.

SymbolIndex used to dominate this list; it was walking every referenced assembly (the whole BCL, every package) and discarding the result. Fixing the walk root cut it ~20–27× with an identical index — so InvocationIndex is now the only meaningful index cost.

The table above is not comparable to the v1/v1.1/v1.2 columns: those were measured against a duetGPT.sln that has since been restructured, and query timings there were taken on a different solution shape. Treat the version columns as history, not as a baseline to diff against.

License

MIT — see LICENSE.

About

C# code intelligence MCP server. Wraps Roslyn MSBuildWorkspace; serves 13 symbol-level navigation tools (find_references, goto_definition, find_implementations, semantic_search, etc.) over stdio to AI coding agents.

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages