Skip to content

Instantly share code, notes, and snippets.

@jrusbatch
Last active July 23, 2026 15:33
Show Gist options
  • Select an option

  • Save jrusbatch/1d2c539ef17476c8703f04a2e9148693 to your computer and use it in GitHub Desktop.

Select an option

Save jrusbatch/1d2c539ef17476c8703f04a2e9148693 to your computer and use it in GitHub Desktop.
Integrating the C# LSP in Claude Code

C# LSP in Claude Code

The roslyn-language-server is a .NET tool that provides rich language features for C# through the Language Server Protocol. It powers editor integrations including the C# extension for Visual Studio Code and C# Dev Kit. It offers several operations that Claude can use to navigate C# code rather than using grep and friends.

The Roslyn LSP supports many operations defined by the language service protocol, but from what I read only these commands are supported by Claude as of v2.1.62.

  • goToDefinition: find where a symbol is defined.
  • findReferences: find all references to a symbol.
  • hover: get documentation and type info for a symbol.
  • documentSymbol: list all symbols in a document.
  • workspaceSymbol: search for symbols by name across the entire workspaced.
  • goToImplementation: find implementations of an interface or abstract method.
  • prepareCallHierarchy: get call hierarchy item at a position.
  • incomingCalls: find all callers of a function/method.
  • outgoingCalls: find all functions/methods called by a function.

The instructions below describe how I integrated the roslyn-language-server into Claude Code to give Claude smarter tools for working with C# in the console. My understanding is that Claude in VS Code (especially as part of Copilot) shares VS Code's LSP.

Instructions

I ran these on Windows using Claude Code v2.1.62. The language server is cross platform so it should work on osx or linux. There might be ways to simplify this but this worked so I stopped fiddling with it.

  1. Set the env variable ENABLE_LSP_TOOL=1 in ~/.claude/settings.json:
{
    "env": { "ENABLE_LSP_TOOL": "1" }
}
  1. Run dotnet tool install --global roslyn-language-server --prerelease. This will put roslyn-language-server.cmd (or .sh?) in your global tools directory (for me, ~/.dotnet/tools/).
  2. Create a local marketplace folder. Name/location of the directory doesn't matter, but I used ~/.claude-custom-plugins/.
  3. Inside your marketplace folder, create a directory .claude-plugin.
  4. In the new directory create marketplace.json with the following content. Note that all slashes should be forward slashes. When providing an absolute path start with / (ex: /Users/{{ Your Name }}. Claude was very finicky about the structure of this document. I went through multiple iterations of this and now that it's working I'm scared to touch it.
{
  "$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
  "name": "local",
  "metadata":{
    "description": "Local plugins for development and testing.",
    "version": "1.0.0",
    "license": "MIT"
  },
  "owner": {
    "name": "{{ Your Name }}"
  },
  "plugins": [
    {
      "name": "roslyn-ls",
      "version": "1.0.0",
      "source": "./plugins/roslyn-ls",
      "description": "LSP using roslyn-language-service.",
      "category": "development",
      "author": { "name": "{{ Your Name }}" },
      "tags": [ "csharp" ],
      "lspServers": {
        "roslyn-ls": {
          "command": "{{Path to the .NET tools folder}}/roslyn-language-server.cmd",
          "args": [
            "--stdio",
            "--autoLoadProjects",
            "--logLevel",
            "Information",
            "--extensionLogDirectory",
            "{{ any directory}}/roslyn-language-service/logs"
          ],
          "transport": "stdio",
          "extensionToLanguage": {
            ".cs": "csharp",
            ".csx": "csharp",
            ".cshtml": "csharp"
          },
          "initializationOptions": {},
          "settings": {},
          "startupTimeout" : 120000
        }
      }
    }
  ]
}
  1. Inside your custom plugins directory, next to the .claude-plugin folder, create the directories plugins/roslyn-ls/ (this must match the source in the marketplace.json file).
  2. Create the JSON files .lsp.json and .claude-plugin/plugin.json. Your final directory structure should look like this:
{{ custom plugins path }}/
|-- .claude-plugin/
|   ʟ marketplace.json
ʟ-- plugins/
    ʟ-- roslyn-ls/
        |-- .lsp.json
        ʟ-- .claude-plugin/
            ʟ plugin.json
  1. The content of .claude-plugin/plugin.json should look like the JSON below. I don't know if all these are necessary, but it at least works for me. I also think these values need to match their respective properties in marketplace.json.
{
  "name": "roslyn-ls",
  "description": "LSP using roslyn-language-service.",
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "{{ Your name }}"
  }
}
  1. The content of .lsp.json properties under the csharp peroperty should match the properties in the roslyn-ls object in marketplace.json.
{
  "csharp": 
  {
    "command": "{{Path to the .NET tools folder}}/roslyn-language-server.cmd",
    "args": [
        "--stdio",
        "--autoLoadProjects",
        "--logLevel",
        "Information",
        "--extensionLogDirectory",
        "{{ any directory}}/roslyn-language-service/logs"
    ],
    "transport": "stdio",
    "extensionToLanguage": {
    ".cs": "csharp",
    ".csx": "csharp",
    ".cshtml": "csharp"
    },
    "initializationOptions": {},
    "settings": {},
    "startupTimeout" : 120000
  }
}
  1. Start claude and run the command /plugin marketplace add {{ absolute path to custom plugins folder }}. Ex. for me it was: /plugin marketplace add /Users/{{me}}/.claude-custom-plugins.
  2. Exit claude and run claude plugin install -s user roslyn-ls.
  3. In claude you can verify the LSP is working with these two commands:
    • /plugin -> select the Installed tab, and you should see roslyn-ls Plugin · local · ✔ enabled.
    • Tell claude to run the C# LSP command documentSymbols on {{ path to some C# file }}. You should see a line like ● LSP(operation: "documentSymbol", file: "src\Vendors\IVendor.cs")

You should be all set!

@nicholasptran

Copy link
Copy Markdown

Hi, I have the understanding that the official Claude plugin csharp-ls uses https://github.com/razzmatazz/csharp-language-server. What is the difference between these two since I understand that project also uses Roslyn as its backend?

this one is the official lsp. it works for me with .net framework47 but csharp-ls didn't

@unsafePtr

unsafePtr commented May 25, 2026

Copy link
Copy Markdown

Worked for me using the official roslyn-language-server https://github.com/unsafePtr/ClaudeCodeRoslynLspProxy

@marcel-jackow

Copy link
Copy Markdown

Recommendations from step 5 of the instructions do not work on Windows. At least for me on Windows 11. It has to be a regular Windows path (starting with a drive letter and using backslashes), so for example c:\\Users\\{username}\\.dotnet\\tools\\roslyn-language-server.cmd (backslash needs to be escaped in JSON by the second backslash). Everything works with such paths. Thank you for the guide.

@nicholasptran

nicholasptran commented Jun 3, 2026 via email

Copy link
Copy Markdown

@aetos382

Copy link
Copy Markdown

If you install dotnet/skills/dotnet, the Roslyn LSP is included out of the box; there is no longer any need to create a plugin yourself.

@Phlow2001

Copy link
Copy Markdown

If you install dotnet/skills/dotnet, the Roslyn LSP is included out of the box; there is no longer any need to create a plugin yourself.

I did not work for me. There's an issue with it that's waiting to be fixed: dotnet/skills#598

I used @unsafePtr 's proxy with a local repo wrapper to pass the correct .sln.

@nicholasptran

Copy link
Copy Markdown

If you install dotnet/skills/dotnet, the Roslyn LSP is included out of the box; there is no longer any need to create a plugin yourself.

I did not work for me. There's an issue with it that's waiting to be fixed: dotnet/skills#598

I used @unsafePtr 's proxy with a local repo wrapper to pass the correct .sln.

it works now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment