[BUG] C# LSP (csharp-ls) not working - missing workspace/configuration and other request handlers

Open 💬 53 comments Opened Jan 5, 2026 by paulditerwich

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

The built-in LSP support doesn't work with csharp-ls (C# language server). The LSP client is missing handlers for several standard LSP requests that csharp-ls requires during initialization:

  1. workspace/configuration - Server requests configuration settings (specifically the solution path)
  2. client/registerCapability - Server dynamically registers capabilities
  3. window/workDoneProgress/create - Server creates progress tokens for indexing feedback

Additionally, the workspace configuration capability is disabled (configuration:!1 in cli.js).

Without these, the server fails to initialize properly and returns empty results for operations like documentSymbol, hover, etc.

What Should Happen?

The LSP client should handle these standard LSP protocol requests so that csharp-ls (and other language servers that use these features) work out of the box.

After manually patching cli.js to add these handlers, all LSP operations work correctly (documentSymbol, hover, goToDefinition, findReferences).

Error Messages/Logs

Steps to Reproduce

  1. Configure csharp-ls plugin in ~/.claude/plugins/csharp-lsp/config.json
  2. Open a C# project with a .sln file
  3. Try any LSP operation on a .cs file, e.g.: LSP documentSymbol
  4. Operation returns empty results or fails

Debug logs show:

  • Server sends workspace/configuration request → no handler
  • Server sends client/registerCapability request → no handler
  • Server sends window/workDoneProgress/create request → no handler

Claude Model

None

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.0.76

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Note: This issue persists even after applying the LSP initialize() fix from:
https://gist.github.com/Zamua/f7ca58ce5dd9ba61279ea195a01b190c

That fix addresses the empty initialize() function (issue #13952), but csharp-ls
still fails because it requires these additional LSP protocol handlers that the
client doesn't implement:

  1. workspace/configuration - needs to return solution path
  2. client/registerCapability - dynamic capability registration
  3. window/workDoneProgress/create - progress token creation

The gist fix enables LSP servers to load, but csharp-ls specifically needs these
request handlers to function. Other LSP servers (like typescript-lsp) may work
without them, but csharp-ls requires all three during initialization.

Workaround: Patch cli.js to:

  1. Enable capabilities.workspace.configuration: true
  2. Add handlers:

```javascript
B.onRequest("workspace/configuration", (P) => {
return P.items.map((b) => {
if (b.section === "csharp") return { solution: "path/to/Solution.sln" };
return {};
});
});
B.onRequest("client/registerCapability", () => null);
B.onRequest("window/workDoneProgress/create", () => null);

Suggested fix: The LSP plugin config already has a solution field - this could be read by the workspace/configuration handler instead of requiring manual patching.

csharp-ls version: 0.19.0

View original on GitHub ↗

53 Comments

b-straub · 6 months ago

So this means that even with the fixes from 2.1.1 the csharp-lsp is still broken and not functional, correct?

paulditerwich · 6 months ago

@b-straub Yes, still broken in 2.1.1 — confirmed 2026-01-08.

I have a working patch.

---

### Root Cause

Claude Code's LSP client needs three handlers that csharp-ls requires:

| Missing Handler | Purpose |
|-----------------|---------|
| workspace/configuration | Provide .sln path |
| client/registerCapability | Dynamic capability registration |
| window/workDoneProgress/create | Progress tokens |

And capabilities.workspace.configuration is disabled.

---

### The Fix

File: ~/.claude/local/node_modules/@anthropic-ai/claude-code/cli.js

1. Enable configuration capability:
```bash
sed -i '' 's/configuration:!1/configuration:!0/g' cli.js

  1. Inject these handlers (before the LSP client started log):

L.onRequest("workspace/configuration", (P) => {
return P.items.map((b) => {
if (b.section === "csharp") {
try {
const sln = require("fs")
.readdirSync(process.cwd())
.find(f => f.endsWith(".sln"));
if (sln) return { solution: sln };
} catch (e) {}
}
return {};
});
});

L.onRequest("client/registerCapability", () => null);
L.onRequest("window/workDoneProgress/create", () => null);

sed -i '' 's/W\.length=0,v(LSP client started for \${A})/W.length=0,L.onRequest("workspace\/configuration",(P)=>{return P.items.map((b)=>{if(b.section==="csharp"){try{const sln=require("fs").readdirSync(process.cwd()).find(f=>f.endsWith(".sln"));if(sln)return{solution:sln}}catch(e){}}return{}})}),L.onRequest("client\/registerCapability",()=>null),L.onRequest("window\/workDoneProgress\/create",()=>null),v(LSP client started for ${A})/' cli.js

Variable names change per version. Find yours:
grep -oE '[A-Z]\.onRequest\("' cli.js | sort | uniq -c
grep -oE '.{20}LSP client started for' cli.js
---
Test Results (after restart)
┌────────────────────┬────────────┬─────┐
│ Operation │ TypeScript │ C# │
├────────────────────┼────────────┼─────┤
│ hover │ ✅ │ ✅ │
├────────────────────┼────────────┼─────┤
│ goToDefinition │ ✅ │ ✅ │
├────────────────────┼────────────┼─────┤
│ documentSymbol │ ✅ │ ✅ │
├────────────────────┼────────────┼─────┤
│ goToImplementation │ — │ ✅ │
├────────────────────┼────────────┼─────┤
│ findReferences │ — │ ✅ │
└────────────────────┴────────────┴─────┘
---

```
Suggested Official Fix

The solution field already exists in the plugin config schema. The LSP client just needs to:

  1. Enable workspace.configuration capability
  2. Return config.solution when workspace/configuration is requested for csharp
  3. Handle client/registerCapability and window/workDoneProgress/create
icanhasjonas · 6 months ago

Alternative workaround: Bun adapter (no cli.js patching)

I hit the same issue and built a different workaround - a transparent proxy that intercepts the problematic requests before they reach Claude Code.

How it works

Instead of patching cli.js, rename the original csharp-ls binary and replace it with a Bun script that:

  1. Spawns the real csharp-ls
  2. Proxies all LSP messages between Claude Code and csharp-ls
  3. Intercepts these three methods and responds with null directly to csharp-ls:
  • window/workDoneProgress/create
  • window/workDoneProgress/cancel
  • client/registerCapability
  1. Passes everything else through (including workspace/configuration which Claude Code handles fine)

Why this approach?

  • Survives updates - cli.js gets replaced on update, the adapter doesn't
  • Non-invasive - doesn't touch Claude Code internals
  • Easy to remove - just swap the binaries back when this is fixed upstream
  • Debuggable - set LSP_ADAPTER_DEBUG=1 to see all LSP traffic

Setup

# Rename original
mv ~/.dotnet/tools/csharp-ls ~/.dotnet/tools/csharp-ls-original

# Create adapter (save the Bun script as ~/.dotnet/tools/csharp-ls)
chmod +x ~/.dotnet/tools/csharp-ls

Full adapter code (~150 lines of Bun/TypeScript): https://gist.github.com/icanhasjonas/4c04328b8c886277399ad23f41549571

Works great here - all LSP operations (documentSymbol, hover, goToDefinition, findReferences, etc.) now work perfectly.

MagnificentS · 6 months ago
@b-straub Yes, still broken in 2.1.1 — confirmed 2026-01-08. I have a working patch. ### Root Cause Claude Code's LSP client needs three handlers that csharp-ls requires: Missing Handler Purpose workspace/configuration Provide .sln path client/registerCapability Dynamic capability registration window/workDoneProgress/create Progress tokens And capabilities.workspace.configuration is disabled. ### The Fix File: ~/.claude/local/node_modules/@anthropic-ai/claude-code/cli.js 1. Enable configuration capability: sed -i '' 's/configuration:!1/configuration:!0/g' cli.js 2. Inject these handlers (before the LSP client started log): L.onRequest("workspace/configuration", (P) => { return P.items.map((b) => { if (b.section === "csharp") { try { const sln = require("fs") .readdirSync(process.cwd()) .find(f => f.endsWith(".sln")); if (sln) return { solution: sln }; } catch (e) {} } return {}; }); }); L.onRequest("client/registerCapability", () => null); L.onRequest("window/workDoneProgress/create", () => null); sed -i '' 's/W\.length=0,v(LSP client started for \${A})/W.length=0,L.onRequest("workspace\/configuration",(P)=>{return P.items.map((b)=>{if(b.section==="csharp"){try{const sln=require("fs").readdirSync(process.cwd()).find(f=>f.endsWith(".sln"));if(sln)return{solution:sln}}catch(e){}}return{}})}),L.onRequest("client\/registerCapability",()=>null),L.onRequest("window\/workDoneProgress\/create",()=>null),v(LSP client started for ${A})/' cli.js Variable names change per version. Find yours: grep -oE '[A-Z]\.onRequest\("' cli.js | sort | uniq -c grep -oE '.{20}LSP client started for' cli.js --- Test Results (after restart) ┌────────────────────┬────────────┬─────┐ │ Operation │ TypeScript │ C# │ ├────────────────────┼────────────┼─────┤ │ hover │ ✅ │ ✅ │ ├────────────────────┼────────────┼─────┤ │ goToDefinition │ ✅ │ ✅ │ ├────────────────────┼────────────┼─────┤ │ documentSymbol │ ✅ │ ✅ │ ├────────────────────┼────────────┼─────┤ │ goToImplementation │ — │ ✅ │ ├────────────────────┼────────────┼─────┤ │ findReferences │ — │ ✅ │ └────────────────────┴────────────┴─────┘ --- Suggested Official Fix The solution field already exists in the plugin config schema. The LSP client just needs to: 1. Enable workspace.configuration capability 2. Return config.solution when workspace/configuration is requested for csharp 3. Handle client/registerCapability and window/workDoneProgress/create

Could you kindly share your file please?

b-straub · 6 months ago

I did install the BUN adapter, but I have no idea how it's supposed to work. The plugin is just a ReadME csharp-lsp and Claud complains that no fitting MCP is present.

CC: I see csharp-lsp@claude-plugins-official is enabled in your settings, but it's not showing as connected in the MCP server list.

paulditerwich · 6 months ago

@MagnificentS Here's the patch script: https://gist.github.com/paulditerwich/ccadc92f026f6c1bce9fbe4f8720c3f8

```bash
curl -fsSL https://gist.githubusercontent.com/paulditerwich/ccadc92f026f6c1bce9fbe4f8720c3f8/raw/csharp-lsp-fix.sh | bash

Creates a backup automatically. Restore with --restore flag.
```

icanhasjonas · 6 months ago
@MagnificentS Here's the patch script: gist.github.com/paulditerwich/ccadc92f026f6c1bce9fbe4f8720c3f8 curl -fsSL https://gist.githubusercontent.com/paulditerwich/ccadc92f026f6c1bce9fbe4f8720c3f8/raw/csharp-lsp-fix.sh | bash Creates a backup automatically. Restore with --restore flag.

Setting Up C# LSP in Claude Code (Complete Guide)

A step-by-step guide to get C# code intelligence working in Claude Code.

Prerequisites

  • Bun installed (curl -fsSL https://bun.sh/install | bash)
  • .NET SDK installed
  • Claude Code installed

Step 1: Install csharp-ls

dotnet tool install --global csharp-ls

Verify it's installed:

~/.dotnet/tools/csharp-ls --version
# Should output: csharp-ls version 0.21.0.0 (or similar)

Step 2: Install the LSP Adapter

Claude Code has a bug where it doesn't handle certain LSP requests, causing csharp-ls to crash. We fix this with a transparent adapter.

2a. Rename the original binary

mv ~/.dotnet/tools/csharp-ls ~/.dotnet/tools/csharp-ls-original

2b. Create the adapter

curl -fsSL https://gist.githubusercontent.com/icanhasjonas/4c04328b8c886277399ad23f41549571/raw/csharp-ls \
  -o ~/.dotnet/tools/csharp-ls

chmod +x ~/.dotnet/tools/csharp-ls

2c. Verify the adapter works

echo "" | LSP_ADAPTER_DEBUG=1 ~/.dotnet/tools/csharp-ls 2>&1 | head -3
# Should output:
# [ADAPTER] Starting csharp-ls adapter...
# [ADAPTER] Spawned csharp-ls, pid: XXXXX

Step 3: Enable the LSP Plugin in Claude Code

Edit ~/.claude/settings.json and add/update the enabledPlugins section:

{
  "enabledPlugins": {
    "csharp-lsp@claude-plugins-official": true
  }
}

Full example ~/.claude/settings.json:

{
  "theme": "dark",
  "enabledPlugins": {
    "csharp-lsp@claude-plugins-official": true,
    "typescript-lsp@claude-plugins-official": true,
    "pyright-lsp@claude-plugins-official": true
  }
}

Step 4: Restart Claude Code

Exit and restart Claude Code for the plugin to load.

Step 5: Test It!

In a C# project, ask Claude to use LSP:

Use LSP documentSymbol on src/MyController.cs

Or Claude will automatically use LSP for code navigation tasks like:

  • "Find where IMyService is defined"
  • "What methods are in this class?"
  • "Find all references to this method"

Troubleshooting

Enable debug logging

LSP_ADAPTER_DEBUG=1 claude

Check logs at ~/.claude/debug/ for [ADAPTER] entries.

"No LSP server available"

  1. Check the plugin is enabled: cat ~/.claude/settings.json | grep csharp
  2. Restart Claude Code
  3. Check debug logs for errors

Verify adapter is being used

grep -i "ADAPTER" ~/.claude/debug/*.txt | tail -10

You should see [ADAPTER] Starting csharp-ls adapter...

What the Adapter Does

The adapter intercepts three LSP methods that Claude Code doesn't handle properly:

| Method | Issue |
|--------|-------|
| window/workDoneProgress/create | Claude Code doesn't implement handler |
| window/workDoneProgress/cancel | Claude Code doesn't implement handler |
| client/registerCapability | Claude Code sends invalid response |

Everything else passes through normally.

Project-Specific Settings (Optional)

For project-specific LSP settings, create .claude/settings.local.json in your project:

{
  "permissions": {
    "allow": [
      "Read(**)"
    ]
  }
}

Available LSP Operations

Once set up, Claude can use these operations:

| Operation | What it does |
|-----------|--------------|
| documentSymbol | List all classes, methods, fields in a file |
| hover | Get type info and documentation |
| goToDefinition | Jump to where something is defined |
| findReferences | Find all usages of a symbol |
| goToImplementation | Find interface implementations |
| workspaceSymbol | Search for symbols across the codebase |

References

b-straub · 6 months ago

There are some problems

  1. your bun code has a hardcoded path const csharpLs = spawn(["/Users/xxxx/.dotnet/tools/csharp-ls-original"], {
  2. dotnet tool install --global csharp-language-server likely you mean "dotnet tool install --global csharp-ls"
  3. the tool is not installed in "tools" but in "toolResolverCache" .NET 10 MacOS

I changed the code to

const csharpLs = spawn(["dotnet", "csharp-ls"], {
stdin: "pipe",
stdout: "pipe",
stderr: "inherit",
});

And Claude debug now showing various log entries for LSP SERVER and LSP PROTOCOL
Last entry: [LSP SERVER plugin:csharp-lsp:csharp-ls] 13:52:58.409 info: Roslyn.Solution[0]
Will use MSBuild props: map [(TargetFramework, net10.0)]

When asking in Rider IDE CC something like:

LSP workspaceSymbol IMyInterface

⏺ The IDE MCP server doesn't provide workspaceSymbol functionality - only getDiagnostics is available.

icanhasjonas · 6 months ago

@b-straub .. oups! missed the hard coded path ;-)

Your change is perfect! Nice catch.
I updated the original comment above..

b-straub · 6 months ago

I still don't have the faintest idea how this is supposed to work, whatever I try in Rider or VS Code CC mentions always that no LSP MCP is installed. For me there is something fundamentally broken with the plugin approach. This should be straightforward to install and use.

b-straub · 6 months ago
Anthropic wiped out LSP in 2.1.0+ from system prompt marckrenn/claude-code-changelog@v2.0.76...v2.1.1#diff-b0a16d13c25d701124251a8943c92de0ff67deacae73de1e83107722f5e5d7f1L741

So basically it means fundamentally broken?

paulditerwich · 6 months ago

C# Patch LSP working (again) on Claude Code 2.1.2

Patch adds the missing workspace/configuration, client/registerCapability, and window/workDoneProgress/create handlers (as sharp-lsp is still broken in version 2.1.2).

```bash
curl -fsSL https://gist.githubusercontent.com/paulditerwich/77448b526e4940f27f6de41421014c02/raw/csharp-lsp-fix-2.1.2.sh | bash

Test Results After Patch
┌────────────────────┬─────┬────────────┐
│ Operation │ C# │ TypeScript │
├────────────────────┼─────┼────────────┤
│ documentSymbol │ ✅ │ ✅ │
├────────────────────┼─────┼────────────┤
│ hover │ ✅ │ ✅ │
├────────────────────┼─────┼────────────┤
│ goToDefinition │ ✅ │ ✅ │
├────────────────────┼─────┼────────────┤
│ findReferences │ ✅ │ ✅ │
├────────────────────┼─────┼────────────┤
│ goToImplementation │ ✅ │ ✅ │
└────────────────────┴─────┴────────────┘
Restore: ./csharp-lsp-fix-2.1.2.sh --restore

Gist: https://gist.github.com/paulditerwich/77448b526e4940f27f6de41421014c02
```

archie1602 · 6 months ago

I managed to fix the C# LSP plugin issue on my setup. Sharing the solution for others who installed Claude Code via Homebrew Cask.

My Environment

  • Claude Code version: 2.1.2
  • Installation method: Homebrew Cask (/opt/homebrew/Caskroom/claude-code/2.1.2/claude)
  • csharp-ls version: 0.21.0.0
  • macOS: Apple Silicon (M4)
  • .NET SDK: 10.0.100

The Problem

The cli.js patch mentioned in this thread doesn't work for Homebrew Cask installations because Claude Code is installed as a compiled binary, not a Node.js application with cli.js.

When I checked the installation:

grep -r "LSP client started" /opt/homebrew/Caskroom/claude-code/2.1.2/ 2>/dev/null
# Output: Binary file /opt/homebrew/Caskroom/claude-code/2.1.2/claude matches

No cli.js file exists to patch.

The Solution: Bun Adapter

Thanks to @icanhasjonas for the Bun adapter approach! This works perfectly for Homebrew Cask installations.

Step 1: Install Bun

curl -fsSL https://bun.sh/install | bash
source ~/.zshrc

Step 2: Rename Original csharp-ls

mv ~/.dotnet/tools/csharp-ls ~/.dotnet/tools/csharp-ls-original

Step 3: Download the Adapter

curl -fsSL https://gist.githubusercontent.com/icanhasjonas/4c04328b8c886277399ad23f41549571/raw/csharp-ls \
  -o ~/.dotnet/tools/csharp-ls

chmod +x ~/.dotnet/tools/csharp-ls

Step 4: Verify the Adapter Works

echo "" | LSP_ADAPTER_DEBUG=1 ~/.dotnet/tools/csharp-ls 2>&1 | head -3

Expected output:

[ADAPTER] Starting csharp-ls adapter...
[ADAPTER] Spawned csharp-ls, pid: XXXXX
[ADAPTER] Client stdin closed

Step 5: Restart Claude Code

Step 6: Test It!

LSP findReferences MyMethod in src/MyFile.cs

Result

All LSP operations now work correctly:

| Operation | Status |
|-----------|--------|
| findReferences | ✅ |
| goToDefinition | ✅ |
| documentSymbol | ✅ |
| hover | ✅ |
| goToImplementation | ✅ |

Reverting Changes

rm ~/.dotnet/tools/csharp-ls
mv ~/.dotnet/tools/csharp-ls-original ~/.dotnet/tools/csharp-ls
nullbio · 6 months ago
I managed to fix the C# LSP plugin issue on my setup. Sharing the solution for others who installed Claude Code via Homebrew Cask. ## My Environment Claude Code version: 2.1.2 Installation method: Homebrew Cask (/opt/homebrew/Caskroom/claude-code/2.1.2/claude) csharp-ls version: 0.21.0.0 macOS: Apple Silicon (M4) * .NET SDK: 10.0.100 ## The Problem The cli.js patch mentioned in this thread doesn't work for Homebrew Cask installations because Claude Code is installed as a compiled binary, not a Node.js application with cli.js. When I checked the installation: grep -r "LSP client started" /opt/homebrew/Caskroom/claude-code/2.1.2/ 2>/dev/null # Output: Binary file /opt/homebrew/Caskroom/claude-code/2.1.2/claude matches No cli.js file exists to patch. ## The Solution: Bun Adapter Thanks to @icanhasjonas for the Bun adapter approach! This works perfectly for Homebrew Cask installations. ### Step 1: Install Bun curl -fsSL https://bun.sh/install | bash source ~/.zshrc ### Step 2: Rename Original csharp-ls mv ~/.dotnet/tools/csharp-ls ~/.dotnet/tools/csharp-ls-original ### Step 3: Download the Adapter curl -fsSL https://gist.githubusercontent.com/icanhasjonas/4c04328b8c886277399ad23f41549571/raw/csharp-ls \ -o ~/.dotnet/tools/csharp-ls chmod +x ~/.dotnet/tools/csharp-ls ### Step 4: Verify the Adapter Works echo "" | LSP_ADAPTER_DEBUG=1 ~/.dotnet/tools/csharp-ls 2>&1 | head -3 Expected output: `` [ADAPTER] Starting csharp-ls adapter... [ADAPTER] Spawned csharp-ls, pid: XXXXX [ADAPTER] Client stdin closed ` ### Step 5: Restart Claude Code ### Step 6: Test It! ` LSP findReferences MyMethod in src/MyFile.cs `` ## Result All LSP operations now work correctly: Operation Status findReferences ✅ goToDefinition ✅ documentSymbol ✅ hover ✅ goToImplementation ✅ ## Reverting Changes rm ~/.dotnet/tools/csharp-ls mv ~/.dotnet/tools/csharp-ls-original ~/.dotnet/tools/csharp-ls

Thank you, this worked for me too.

nullbio · 6 months ago

Hmm, actually... Verifying the adapter worked, but Claude Code still can't use the csharp-lsp plugin for some reason, despite it saying it's installed and running.

❯ can you access the csharp-lsp? you have a csharp-lsp plugin installed.                                                                                                                                                         
● You're right that there may be a csharp-lsp plugin installed, but I don't see its tools available in my current session. The MCP tools I have access to are:                                    
  - mcp__plugin_context7_context7__* (documentation lookup)                                                                                                                                         - mcp__plugin_playwright_playwright__* (browser automation)
                                                                                                                                                                                                    The csharp-lsp tools aren't appearing in my available functions. This could mean:
  1. The plugin needs to be restarted/reloaded
  2. There's a configuration issue
  3. The LSP server isn't running

  You might need to restart Claude Code or check the plugin configuration to make the C# LSP tools available to me.

Any idea how to fix this?

b-straub · 6 months ago
Hmm, actually... Verifying the adapter worked, but Claude Code still can't use the csharp-lsp plugin for some reason, despite it saying it's installed and running. `` ❯ can you access the csharp-lsp? you have a csharp-lsp plugin installed. ● You're right that there may be a csharp-lsp plugin installed, but I don't see its tools available in my current session. The MCP tools I have access to are: - mcp__plugin_context7_context7__* (documentation lookup) - mcp__plugin_playwright_playwright__* (browser automation) The csharp-lsp tools aren't appearing in my available functions. This could mean: 1. The plugin needs to be restarted/reloaded 2. There's a configuration issue 3. The LSP server isn't running You might need to restart Claude Code or check the plugin configuration to make the C# LSP tools available to me. `` Any idea how to fix this?

Absolutely same for me!

nullbio · 6 months ago
> Hmm, actually... Verifying the adapter worked, but Claude Code still can't use the csharp-lsp plugin for some reason, despite it saying it's installed and running. > `` > ❯ can you access the csharp-lsp? you have a csharp-lsp plugin installed. > ● You're right that there may be a csharp-lsp plugin installed, but I don't see its tools available in my current session. The MCP tools I have access to are: > - mcp__plugin_context7_context7__* (documentation lookup) - mcp__plugin_playwright_playwright__* (browser automation) > The csharp-lsp tools aren't appearing in my available functions. This could mean: > 1. The plugin needs to be restarted/reloaded > 2. There's a configuration issue > 3. The LSP server isn't running > > You might need to restart Claude Code or check the plugin configuration to make the C# LSP tools available to me. > `` > > > > > > > > > > > > Any idea how to fix this? Absolutely same for me!

I got it working. The trick is to run with LSP_ADAPTER_DEBUG=1 claude, and then look at the ~/.claude/debug/latest log. In there, you'll see a bunch of errors. Paste them to Claude Code and have him help you fix them. For me, I needed:

export DOTNET_ROOT="$(brew --prefix dotnet)/libexec"

and I had Claude make some adjustments to the proxy/wrapper script for csharp-ls at ~/.dotnet/tools/csharp-ls (move the old one to csharp-ls-original if you haven't already) -- here's the updated script, it fixed some passthrough bugs:

#!/usr/bin/env bun
/**
 * LSP Adapter for csharp-ls
 *
 * Proxies LSP messages between Claude Code and csharp-ls,
 * handling unsupported methods that crash csharp-ls.
 *
 * Intercepts (responds with null to csharp-ls, doesn't forward to Claude):
 * - window/workDoneProgress/create -> progress token creation
 * - window/workDoneProgress/cancel -> progress cancellation
 * - client/registerCapability -> dynamic capability registration
 *
 * Passes through (Claude Code handles these):
 * - workspace/configuration -> Claude Code returns config
 * - Everything else
 */

import { spawn } from "bun";
import { dirname, join } from "path";

const DEBUG = process.env.LSP_ADAPTER_DEBUG === "1";

// Find the original csharp-ls binary (sibling to this script)
const SCRIPT_DIR = dirname(Bun.main);
const CSHARP_LS_ORIGINAL = join(SCRIPT_DIR, "csharp-ls-original");

function log(...args: any[]) {
	if (DEBUG) {
		console.error("[ADAPTER]", ...args);
	}
}

// LSP Message parser state
class LspParser {
	private buffer = Buffer.alloc(0);
	private contentLength = -1;

	parse(chunk: Buffer): Array<{ header: string; body: any }> {
		this.buffer = Buffer.concat([this.buffer, chunk]);
		const messages: Array<{ header: string; body: any }> = [];

		while (true) {
			if (this.contentLength === -1) {
				// Looking for Content-Length header
				const headerEnd = this.buffer.indexOf("\r\n\r\n");
				if (headerEnd === -1) break;

				const header = this.buffer.subarray(0, headerEnd).toString();
				const match = header.match(/Content-Length:\s*(\d+)/i);
				if (!match) {
					log("Invalid header, resetting buffer:", header);
					this.buffer = Buffer.alloc(0);  // Prevent infinite loop
					break;
				}

				this.contentLength = parseInt(match[1], 10);
				this.buffer = this.buffer.subarray(headerEnd + 4);
			}

			if (this.buffer.length < this.contentLength) break;

			const bodyStr = this.buffer.subarray(0, this.contentLength).toString();
			this.buffer = this.buffer.subarray(this.contentLength);
			this.contentLength = -1;

			try {
				const body = JSON.parse(bodyStr);
				messages.push({
					header: `Content-Length: ${bodyStr.length}\r\n\r\n`,
					body
				});
			} catch (e) {
				log("Failed to parse JSON:", bodyStr);
			}
		}

		return messages;
	}
}

function encodeMessage(body: any): Buffer {
	const content = JSON.stringify(body);
	const header = `Content-Length: ${Buffer.byteLength(content)}\r\n\r\n`;
	return Buffer.from(header + content);
}

// Methods we intercept and handle ourselves (respond with null, don't forward to Claude)
const INTERCEPTED_METHODS = new Set([
	"window/workDoneProgress/create",
	"window/workDoneProgress/cancel",
	"client/registerCapability",
]);

async function main() {
	// If any CLI args, passthrough directly (LSP mode has no args)
	const args = process.argv.slice(2);
	if (args.length > 0) {
		const result = Bun.spawnSync([CSHARP_LS_ORIGINAL, ...args], {
			stdin: "inherit",
			stdout: "inherit",
			stderr: "inherit",
		});
		process.exit(result.exitCode ?? 1);
	}

	log("Starting csharp-ls adapter...");

	// Spawn the real csharp-ls (original binary renamed, sibling to this script)
	const csharpLs = spawn([CSHARP_LS_ORIGINAL], {
		stdin: "pipe",
		stdout: "pipe",
		stderr: "inherit",
	});

	log("Spawned csharp-ls, pid:", csharpLs.pid);

	const clientParser = new LspParser(); // Claude Code -> Adapter
	const serverParser = new LspParser(); // csharp-ls -> Adapter

	// Forward stdin (from Claude Code) to csharp-ls
	(async () => {
		const reader = Bun.stdin.stream().getReader();
		while (true) {
			const { done, value } = await reader.read();
			if (done) {
				log("Client stdin closed");
				csharpLs.stdin.end();
				break;
			}

			const messages = clientParser.parse(Buffer.from(value));
			for (const msg of messages) {
				log("CLIENT ->", msg.body.method || msg.body.id);
				// Pass all client messages through unchanged
				csharpLs.stdin.write(encodeMessage(msg.body));
			}

			if (messages.length === 0 && value.length > 0) {
				// Partial message, buffer is handling it
			}
		}
	})();

	// Forward stdout (from csharp-ls) to Claude Code, intercepting specific methods
	(async () => {
		const reader = csharpLs.stdout.getReader();
		while (true) {
			const { done, value } = await reader.read();
			if (done) {
				log("Server stdout closed");
				break;
			}

			const messages = serverParser.parse(Buffer.from(value));
			for (const msg of messages) {
				const { body } = msg;

				// Check if this is a request we should intercept
				if (body.method && INTERCEPTED_METHODS.has(body.method)) {
					log("INTERCEPT <-", body.method, "id:", body.id);

					// Send success response back to csharp-ls
					if (body.id !== undefined) {
						const response = {
							jsonrpc: "2.0",
							id: body.id,
							result: null
						};
						csharpLs.stdin.write(encodeMessage(response));
						log("INTERCEPT ->", "response for", body.method);
					}
					// Don't forward to client
					continue;
				}

				log("SERVER ->", body.method || `response:${body.id}`);
				// Forward to Claude Code
				process.stdout.write(encodeMessage(body));
			}
		}
	})();

	// Handle process termination
	process.on("SIGINT", () => {
		log("SIGINT received, killing csharp-ls");
		csharpLs.kill();
		process.exit(0);
	});

	process.on("SIGTERM", () => {
		log("SIGTERM received, killing csharp-ls");
		csharpLs.kill();
		process.exit(0);
	});

	// Wait for csharp-ls to exit
	const exitCode = await csharpLs.exited;
	log("csharp-ls exited with code:", exitCode);
	process.exit(exitCode);
}

main().catch((err) => {
	console.error("[ADAPTER] Fatal error:", err);
	process.exit(1);
});

Also because I'm developing on WSL but writing a C# app for Windows, I also had to add this to my project root:

Filename: Directory.Build.props

<Project>
  <PropertyGroup>
    <EnableWindowsTargeting>true</EnableWindowsTargeting>
  </PropertyGroup>
</Project>

Working perfectly for me now.

chris5335 · 6 months ago

We had this issue in Windows, both running in WSL and the Claude Code for Windows version as well as in Mac and Linux. I gave CC the script @nullbio used and we wrote it in node instead of bun. It works now for everyone but windows people, they are still debugging through it.

peterdrier · 6 months ago

This (csharp-lsp plugin) is not working on windows native cli in v2.1.7. dotnet 10, csharp-ls 0.21.0.0

LuisOsta · 6 months ago

Similar Issue with rust-analyzer (Progress Notifications)

I'm experiencing a related issue with rust-analyzer on macOS. While rust-analyzer doesn't crash like csharp-ls does, the missing window/workDoneProgress/create handler causes a degraded user experience.

The Problem

When starting a new Claude Code session in a large Rust monorepo (~100+ crates):

  1. LSP queries during indexing return empty results ("No symbols found", "No hover information")
  2. No indication that rust-analyzer is still warming up vs. actually having no results
  3. After 30-60+ seconds, the same queries work correctly

This leads to confusion about whether:

  • The LSP server is still indexing (wait and retry)
  • The server failed to start (check configuration)
  • There genuinely are no results (move on)

Technical Analysis

I verified via debug logs that Claude Code receives various LSP notifications but no $/progress notifications:

[DEBUG] [LSP PROTOCOL] Received notification 'textDocument/publishDiagnostics'.
# No $/progress notifications logged despite rust-analyzer sending them

rust-analyzer uses window/workDoneProgress/create to create progress tokens before sending $/progress notifications. Without Claude Code responding to the create request, rust-analyzer can't report indexing progress.

Difference from csharp-ls

| Server | Behavior without handler |
|--------|-------------------------|
| csharp-ls | Crashes - requires handler to function |
| rust-analyzer | Works - but silently fails to report progress |

rust-analyzer is more lenient and continues without progress reporting, but users get no feedback during the 30-60+ second indexing period.

Suggested Fix Scope

Adding the window/workDoneProgress/create handler (as described in this issue) would:

  1. Fix csharp-ls completely (currently broken)
  2. Enable progress reporting for rust-analyzer, jdtls, clangd, and other indexing-heavy LSPs

Ideally, Claude Code would also surface $/progress notifications in the UI so users know when LSP servers are ready.

Environment

  • Claude Code: 2.1.7 (npm install)
  • rust-analyzer: 1.90.0
  • Platform: macOS Darwin 25.2.0
  • Plugin: rust-analyzer-lsp@claude-plugins-official
zzxyz · 5 months ago

Yeah very broken on Windows too. No plugin.json created by installer. Default plugin.json is broken and lines have to be deleted until it processes correctly. Missing handlers required by csharp-ls

Agasper · 5 months ago

.NET-based adapter (no Bun required)

For some reason the Bun adapter didn't work for me, so I created a native .NET adapter that solves the same problem:

CSharpLspAdapter

Easiest setup

Just ask Claude Code to do it for you:

Install C# LSP support following https://github.com/Agasper/CSharpLspAdapter

Claude will read the instructions and configure everything automatically.

Manual install

dotnet tool install --global CSharpLspAdapter

Then configure as a Claude Code plugin (see README).

How it works

Transparent proxy that intercepts problematic LSP methods (workspace/configuration, client/registerCapability, window/workDoneProgress/create) and responds appropriately.

All LSP operations work: documentSymbol, hover, goToDefinition, findReferences, etc.

kmgallahan · 5 months ago

@Agasper Couldn't make your solution work either. I'm just going to use Serena to hit the LSP instead of trying to get at it directly for now.

Agasper · 5 months ago

@kmgallahan What issues do you have ?

Kobus-Smit · 5 months ago

@Agasper Thanks for the adapter. I'd love to get LSP working too.

Does your CSharpLspAdapter work with Claude Code on Windows?

Here is what Claude thinks so far:

Root Cause: Claude Code uses JavaScript string interpolation file://${path} instead of Node.js's pathToFileURL(), producing malformed URIs on Windows.

Your csharp-lsp-adapter.log logs show the exact pattern:

  • didOpen: file://C:\\Dev\\... (malformed, 2 slashes + backslashes)
  • documentSymbol: file:///C:/Dev/... (correct, 3 slashes + forward slashes)

● I've created a plan for implementing URI normalization in the adapter. The plan outlines:

  1. Root cause: Claude Code generates inconsistent file URIs on Windows (same as Issue #16729)
  2. Recommended fix: Modify the adapter to normalize all URIs before forwarding to csharp-ls
  3. Alternative: Patch cli.js directly (less sustainable)

● User approved Claude's plan
⎿  Plan saved to: ~\.claude\plans\wild-prancing-whistle.md · /plan to edit
Plan: Fix C# LSP URI Normalization

Problem Summary

Claude Code's LSP client generates inconsistent file URIs on Windows:

  • didOpen sends: file://C:\\Dev\\... (malformed)
  • documentSymbol sends: file:///C:/Dev/... (correct)

csharp-ls doesn't recognize these as the same file, so all file-specific operations (documentSymbol, hover, goToDefinition, findReferences) return null.

This is the same root cause as https://github.com/anthropics/claude-code/issues/16729.

Recommended Approach: Adapter URI Normalization

Modify the CSharpLspAdapter to normalize all URIs to RFC 8089-compliant format before forwarding to csharp-ls.

Implementation Steps

  1. Clone the adapter repository

git clone https://github.com/Agasper/CSharpLspAdapter.git
cd CSharpLspAdapter

  1. Add URI normalization function in Program.cs:

static string NormalizeFileUri(string uri)
{
// Convert file://C:\path or file://C:/path to file:///C:/path
if (uri.StartsWith("file://") && !uri.StartsWith("file:///"))
{
var path = uri.Substring(7); // Remove "file://"
path = path.Replace("\\", "/");
return "file:///" + path;
}
return uri;
}

  1. Apply normalization in ProcessClientMessages before forwarding:
  • Parse JSON message
  • If message contains textDocument.uri or uri fields, normalize them
  • Forward normalized message to server
  1. Build and install

dotnet pack -c Release
dotnet tool update -g csharplspadapter --add-source ./nupkg

  1. Restart Claude Code to pick up the changes

Files to Modify
┌────────────┬───────────────────────────────────────────────────────────┐
│ File │ Change │
├────────────┼───────────────────────────────────────────────────────────┤
│ Program.cs │ Add NormalizeFileUri() function, apply to client messages │
└────────────┴───────────────────────────────────────────────────────────┘
Verification

After implementation, test these LSP operations:

  • documentSymbol - should return class/method list
  • hover - should return type information
  • goToDefinition - should navigate to definitions
  • findReferences - should find usages

Check C:\Users\KS\AppData\Local\Temp\csharp-lsp-adapter.log to verify URIs are normalized.

Alternative: Patch cli.js Directly

If modifying the adapter is not preferred, the fix from Issue #16729 can be applied to ~/.claude/local/node_modules/@anthropic-ai/claude-code/cli.js:

  1. Find all file://${...} patterns
  2. Replace with pathToFileURL(...).href

Note: This fix will be overwritten when Claude Code updates.

kmgallahan · 5 months ago

@Agasper I pointed CC at the readme and used 3 sessions to try to make it work. They could never form a connection to use the LSP directly. If CC/opus 4.5 couldn't figure out how to make it work, I didn't have a chance.

Noticed above me mention Windows issues - that may be the issue here.

Martin-Andersen · 5 months ago

Please fix it. claude needs the LSP. right now Jetbrains Junie is better

I am using dotnet 10 on a fully updated win11 pc

billyweisberg · 5 months ago

@Agasper Claude struggled to install it. I followed the steps manually and installed .NET 8. Then, it worked. I'm on a mac.
--- CLAUDE
Yes, the LSP appears to be working now! I noticed the IDE diagnostics showing hints like "Use primary constructor" on the new files I created. Those hints are coming from the C# LSP analyzing the code.

The LSP is providing:

  • Code analysis hints (like the primary constructor suggestions)
  • Real-time diagnostics on the files

The csharp-ls-adapter plugin is functioning after we installed .NET 8 runtime.
---
Thanks for providing the adapter.

Agasper · 5 months ago

I just merged PR from @Kobus-Smit and updated NuGet package to version 1.0.2, so update it with
dotnet tool update --global CSharpLspAdapter
and try again

PS Sorry, I don't have Windows to test it

Martin-Andersen · 5 months ago

Started this morning with updating claude and the csharp-lsp plugin, still not working

kmgallahan · 5 months ago

@Agasper I managed to get the adapter working. As others have said, for whatever reason Claude Code + Opus 4.5 just does not understand how to follow the instructions in the readme to get it working properly on its own.

Thank you for your work here 👍

Agasper · 5 months ago

@kmgallahan It's weird, because i tested it with my claude and it completed setup successfully. Maybe if you open an issue in CSharpLspAdapter repo with details i could fix it

oleg-varlamov · 5 months ago

The С# LSP plugin still doesn't work on version 2.1.33 (Windows 11). Workarounds don't help either, and Cloud still claims he can't access LSP.

kmgallahan · 5 months ago

csharp-ls had a release that was supposed to have fixed something:

https://github.com/razzmatazz/csharp-language-server/releases/tag/0.22.0

But I have not tested it and I'm unsure if the plugin uses the new version. If someone wants to investigate that'd be great.

jairbubbles · 5 months ago

It's called by command line so you just need to update it manually no?

kmgallahan · 5 months ago

@jairbubbles I'm unsure how updates work with the plugin.

I just know I spent enough time getting the adapter to work that I don't want to toss that to investigate this.

Agasper · 5 months ago

@oleg-varlamov if your claude claims it can't access LSP then you probably forgot to enable LSP support for claude with environment variable

macOS/Linux

Add this line to your ~/.bashrc or ~/.zshrc file:
export ENABLE_LSP_TOOL=1

Then restart your terminal or run source ~/.zshrc.

Windows
Add ENABLE_LSP_TOOL with value 1 to your user environment variables (System Properties → Environment Variables), then restart Claude Code.

martinalderson · 5 months ago
csharp-ls had a release that was supposed to have fixed something: https://github.com/razzmatazz/csharp-language-server/releases/tag/0.22.0 But I have not tested it and I'm unsure if the plugin uses the new version. If someone wants to investigate that'd be great.

Hi this works for me - pretty much out of the box (I think, I spent a while hacking but updating this to 0.22.0 + env variable cracked it). Just had to add the env variable hint.

Thanks for everyone that's looked into this - was super excited to see this.

TaylorWatson · 5 months ago

None of this worked for me. What ended up working for my mac was creating a symlink because the csharp-ls was in the wrong folder for me.

sudo ln -s /Users/{USER}/.dotnet/tools/csharp-ls /usr/local/bin/csharp-ls

kohowski · 5 months ago
csharp-ls had a release that was supposed to have fixed something: https://github.com/razzmatazz/csharp-language-server/releases/tag/0.22.0 But I have not tested it and I'm unsure if the plugin uses the new version. If someone wants to investigate that'd be great.

Without applying any patches discussed here, I now get no errors in cc debug log from start, but references/goToDefinition return immediately with no results. The solution is never discovered by LSP.

ajdrobnich · 5 months ago

This does not solve csharp-ls specifically, but figured I'd share as I did have some luck today with Microsoft's official .NET LSP that they started pushing to nuget as of a couple weeks ago: https://www.nuget.org/packages/roslyn-language-server. This was using Claude Code 2.1.37 on Windows.

I installed via dotnet tool install --global per the readme and setup a local plugin marketplace for Claude Code with a custom LSP plugin. Here's my plugin.json for reference:

{
  "name": "csharp-roslyn-language-server",
  "description": "C# language support via roslyn-language-server",
  "version": "1.0.0",
  "lspServers": {
    "csharp": {
      "command": "roslyn-language-server.cmd",
      "args": [
        "--stdio",
        "--autoLoadProjects"
      ],
      "extensionToLanguage": {
        ".cs": "csharp",
        ".csx": "csharp"
      }
    }
  }
}

Here are some results from trying it out. As others pointed out above, I did have to prompt it to use the LSP. Otherwise, it still seems to prefer grep and other file operations:

❯ Use LSP to show all usages of PostCommentAsync 

● LSP(operation: "findReferences", symbol: "PostCommentAsync", in:
     "src\CodeReview.Core\Services\BitBucketService.cs")
  ⎿  Found 9 references across 5 files (ctrl+o to expand)

● Here are all 9 usages of PostCommentAsync across 5 files:

  Interface declaration:
  - src/CodeReview.Core/Interfaces/IBitBucketService.cs:22 — Interface method
  definition

  Implementation:
  - src/CodeReview.Core/Services/BitBucketService.cs:85 — Method implementation

  Production usage:
  - src/CodeReview.Core/Services/ReviewOrchestrator.cs:139 — Posts review 
  comment
  - src/CodeReview.Core/Services/ReviewOrchestrator.cs:192 — Posts review 
  comment

  Unit tests:
  - tests/CodeReview.Core.Tests/ReviewOrchestratorTests.cs:79
  - tests/CodeReview.Core.Tests/ReviewOrchestratorTests.cs:163
  - tests/CodeReview.Core.Tests/ReviewOrchestratorTests.cs:228

  Integration tests:
  - tests/CodeReview.Integration.Tests/EndToEndReviewTests.cs:129
  - tests/CodeReview.Integration.Tests/EndToEndReviewTests.cs:203

❯ What are the methods in                                                   
  @src\CodeReview.Core\Interfaces\IBitBucketService.cs                      
  ⎿  Read src\CodeReview.Core\Interfaces\IBitBucketService.cs (50 lines)

● Based on the file content, here are the 4 methods in IBitBucketService.cs:

  1. GetPullRequestDiffAsync() (line 13) — Task<string> — Fetches the diff for a
   pull request.
  2. PostCommentAsync() (line 22) — Task — Posts a general comment to a pull
  request.
  3. PostInlineCommentAsync() (line 32) — Task — Posts an inline comment to a
  specific file and line in a pull request.
  4. GetPullRequestAsync() (line 44) — Task<PullRequestInfo> — Gets pull request
   details.

  All methods share the same parameter pattern: workspace, repositorySlug, 
  pullRequestId, plus method-specific parameters, and an optional
  CancellationToken.

❯ Use LSP to goto definition of PostCommentAsync 

● LSP(operation: "goToDefinition", symbol: "PostCommentAsync", in:
     "src\CodeReview.Core\Interfaces\IBitBucketService.cs")
  ⎿  Found 1 definition (ctrl+o to expand)

● The definition of PostCommentAsync is in
  src/CodeReview.Core/Interfaces/IBitBucketService.cs:22 — which is the
  interface declaration itself. Since this is the interface method, it is the
  definition.

  To find the implementation instead, I can use goToImplementation:

● LSP(operation: "goToImplementation", symbol: "PostCommentAsync", in:
     "src\CodeReview.Core\Interfaces\IBitBucketService.cs")
  ⎿  Found 1 implementation (ctrl+o to expand)

● - Definition (interface):
  src/CodeReview.Core/Interfaces/IBitBucketService.cs:22
  - Implementation: src/CodeReview.Core/Services/BitBucketService.cs:85

The nuget is prerelease, so I imagine there will be some stability issues. But hopefully this helps somebody.

kmgallahan · 5 months ago

@ajdrobnich Hahah, I did the same thing today, but realized it wasn't going to be enough. Here is the core problem:

Anthropic devs are too busy with a million things that they don't have time to resolve the thousands of issues with Claude Code, let alone better LSP support, even further proper Roslyn LSP support. I mean, they vibe coded C# LSP support using a 3rd party repo that didn't even work for the first month...

The LSP 3.17 spec defines 30+ capabilities with many parameters and client options. Claude Code can only use 9 of them, and with a limited option set -- this is a built-in problem with how it can call its LSP tool.

The MS Roslyn LSP CLI meanwhile provides 24 capabilities, only 6 of which Claude Code can actually call via its LSP tool. One important LSP capability Roslyn doesn't provide yet (draft PR is pending), and that is call hierarchy stuff, which also impacts 2 other capabilities. Those are 3 of the 9 LSP capabilities CC can use.

So in the end, CC can only use 6 of the 24 C#/Roslyn LSP capabilities.

---

My solution - since this is 2026 and this was not that difficult, I just had Claude build a custom MCP server that exposes ALL of the Roslyn CLI LSP capabilities, using all params and client options. This completely bypasses the built-in CC limitations on LSP usage.

It basically acts like a proxy that takes CC MCP tool calls, pipes them to the Roslyn CLI LSP process, and handles the output piping it right back. Also has to take care of initialization concerns and other things that are less obvious.

One important finding - many of the capabilities exposed are based on feeding in character positions as params, and CC frequently has issues figuring out the exact right positions to use. An interesting quirk.

IcePower · 5 months ago
This does not solve csharp-ls specifically, but figured I'd share as I did have some luck today with Microsoft's official .NET LSP that they started pushing to nuget as of a couple weeks ago: https://www.nuget.org/packages/roslyn-language-server. This was using Claude Code 2.1.37 on Windows.

I tried your approach and it works — it can use goToDefinition and findReferences.
But I found an issue: after I let CC edit a C# file, the LSP starts throwing an error:
Error performing hover: Cannot send request to LSP server 'plugin:csharp-roslyn-lsp:csharp': server is running

I asked CC to analyze it, and it suggested the possible causes might be:

1. A TextDocumentSync mode mismatch — Roslyn may expect incremental sync (Incremental), but Claude Code is sending full sync (Full), or vice versa. 2. Claude Code’s Edit tool might bypass the LSP didChange channel — it may write directly to disk and then send a notification in an unexpected format. 3. The Roslyn server tries to reload the project while processing the change, but a deadlock occurs on the stdio channel.

Have you run into this issue before?

ajdrobnich · 5 months ago
Have you run into this issue before?

I hadn't but did end up eventually run into it. I reverted back to the CSharpLspAdapter nuget mentioned earlier. I had more luck with that today.

IcePower · 5 months ago
> Have you run into this issue before? I hadn't but did end up eventually run into it. I reverted back to the CSharpLspAdapter nuget mentioned earlier. I had more luck with that today.

Thank you for your reply. I also tried the CSharpLspAdapter, and it indeed works perfectly!
I think this is the best solution for now!

kmgallahan · 5 months ago

FYI, it fails to work on session restore + /resume.

https://github.com/anthropics/claude-code/issues/24171

egaf-lorenzol · 4 months ago

I'm currently using this MCP server, works smooth like butter: https://marketplace.visualstudio.com/items?itemName=LadislavSopko.mcpserverforvs

> > Have you run into this issue before? > > > I hadn't but did end up eventually run into it. I reverted back to the CSharpLspAdapter nuget mentioned earlier. I had more luck with that today. Thank you for your reply. I also tried the CSharpLspAdapter, and it indeed works perfectly! I think this is the best solution for now!
razzmatazz · 3 months ago

I believe all of the issues reported here related to csharp-ls have been fixed in 0.24.0 and no adapter is needed anymore.

jmbryan4 · 2 months ago

opencode swapped to use roslyn-language-server LSP over csharp-ls in this release: https://github.com/anomalyco/opencode/releases/tag/v1.14.21

unsafePtr · 1 month ago

For anyone landing here looking for a working setup: I shipped a small proxy that makes the Roslyn LSP (Microsoft.CodeAnalysis.LanguageServer) work inside Claude Code by injecting the solution/open notification that the built-in LSP client doesn't send. With it, findReferences, goToDefinition, goToImplementation, hover, documentSymbol, workspaceSymbol, and call-hierarchy work solution-wide on .slnx / .sln / loose .csproj workspaces.

!Solution-wide findReferences and goToImplementation in Claude Code, with the proxy active

Repo: https://github.com/unsafePtr/ClaudeCodeRoslynLspProxy

Caveats: needs ENABLE_LSP_TOOL=1 in the user's ~/.claude/settings.json env block (the LSP tool is gated today), and only the read-only LSP operations are surfaced by Claude Code — edit operations like rename / codeAction / formatting are implemented by Roslyn but not exposed by Claude Code's LSP tool yet.

KyleSlusser · 1 month ago

Confirmed this is working again after updating csharp-ls to the latest version. No adapter needed.

firish · 8 days ago

Workaround if you are on Windows with Visual Studio available: a community extension that gives Claude real C# semantics from Roslyn instead of an LSP shim: find-all-references, go-to-definition, implementations, and call/type hierarchies, resolved through interfaces and overloads, plus decompiling methods in referenced DLLs. It rides an MCP server, so the CLI picks the tools up automatically. https://github.com/firish/claude_code_vs (docs/SEMANTIC.md)

Not a fix for csharp-ls itself but might be relevant.