claude-csharp-lsp
Solution-aware C# (Roslyn) language intelligence for Claude Code, with multi-repo and multi-solution workspaces handled as a first-class case.

findReferences, goToDefinition, goToImplementation, hover, and call hierarchy that resolve across your whole solution, including when your Claude Code workspace contains several repositories side by side.
The problem
Claude Code can talk to language servers, but its LSP client does not send Microsoft's Roslyn server the solution/open notification it needs to load your code as a unified solution graph. Without it, Roslyn runs in per-file mode, so cross-file queries come back empty or partial: findReferences finds only the declaration, goToImplementation finds nothing. If you have tried the stock C# LSP plugins and watched them return nothing, this is why.
If you open multiple repos in one workspace (a common setup), the existing workarounds also fall over: they pick one solution arbitrarily and leave the rest blind.
The fix
claude-csharp-lsp is a small, zero-dependency Node proxy that sits between Claude Code and Roslyn. It passes the LSP handshake through untouched, then, once the client is ready, discovers what to open and injects the missing notification:
Claude Code <-> claude-csharp-lsp <-> Microsoft Roslyn language server
|
after `initialized`: sends solution/open or project/open
The discovery logic is what makes it work everywhere:
| Your workspace | What it loads |
|---|
| One solution at the root | that solution (solution/open) |
| One solution anywhere in the tree | that solution (solution/open) |
| Several repos or solutions side by side | every project across all of them (project/open), with no hand-authored master solution required |
No solution, just .csproj files | all of those projects |
That third row is the point: multi-repo works with no setup, and you get full cross-file navigation in every repo.
Example
A workspace with three repos open side by side: shared/ defines a method, orders/ and billing/ each call it.
> Find all references to PricingService.Calculate across the workspace.
shared/Pricing/PricingService.cs:4 public static decimal Calculate(decimal subtotal, decimal taxRate)
orders/Orders/Program.cs:2 var total = PricingService.Calculate(100m, 0.08m);
billing/Billing/Program.cs:2 var invoice = PricingService.Calculate(250m, 0.05m);
3 references across 3 repositories
Without the plugin, the same query returns only the declaration in PricingService.cs: Roslyn never loaded the other projects, so the call sites are invisible.
Requirements
Install
Enable Claude Code's LSP tool once, then add the plugin:
// ~/.claude/settings.json
{ "env": { "ENABLE_LSP_TOOL": "1" } }
/plugin marketplace add Webority/claude-csharp-lsp
/plugin install csharp-lsp@claude-csharp-lsp
Then fully restart Claude Code (the language server is launched fresh on start). Open any .cs file and ask Claude to find references, go to definition, or list implementations.
The proxy ships inside the plugin, so there is nothing else to install: no NuGet package and no binary download. /plugin marketplace add delivers everything.
Run /csharp-lsp:doctor at any time to verify your setup. It checks the .NET SDK, the Roslyn language server and its PATH entry, ENABLE_LSP_TOOL, and that only one C# language server is active, and it can fix the common issues for you.
Supported operations
Claude Code's LSP tool surfaces read and navigation operations, all of which this proxy makes solution-aware:
findReferences, goToDefinition, goToImplementation, hover, documentSymbol, prepareCallHierarchy, incomingCalls, outgoingCalls.
These apply to C# (.cs) and to Razor/Blazor components (.razor, .cshtml). Roslyn serves the C# inside Razor @code blocks via cohosting, so navigation works inside components too.
LSP-based edits (rename, code actions, formatting) are not exposed by Claude Code's tool, so they are out of scope. Claude edits files directly and you verify with dotnet build.
How it works