[FEATURE] Improve LSP client compatibility with Roslyn language server (C#/.NET)

Resolved 💬 6 comments Opened Mar 25, 2026 by jizc Closed Jun 3, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet
  • [x] This is a single feature request (not multiple features)

Problem Statement

The official Microsoft Roslyn language server (Microsoft.CodeAnalysis.LanguageServer) only partially works with Claude Code's LSP client. Some operations succeed (hover, documentSymbol, goToDefinition, findReferences) but others fail or return empty results (workspaceSymbol, goToImplementation), and indexing is unreliable — operations called shortly after startup often fail.

This issue focuses on Roslyn-specific protocol requirements that go beyond the generic LSP gaps already tracked in other issues.

What works

  • hover — returns type info
  • documentSymbol — lists symbols in a file
  • goToDefinition — navigates to definitions within the workspace
  • findReferences — finds references (once indexing completes)

What doesn't work

  • goToImplementation — Roslyn won't enable this without textDocument.implementation in client capabilities (see #32502)
  • workspaceSymbol — requires a query parameter the LSP tool doesn't expose (see #38198), and workspace.symbol is missing from client capabilities
  • callHierarchy / incomingCalls / outgoingCalls — Roslyn doesn't implement textDocument/prepareCallHierarchy; this is a server limitation, not a Claude Code bug
  • Indexing is unreliable — operations fail with empty results when called before Roslyn finishes indexing

Root causes (from binary analysis + Serena comparison)

I compared Claude Code's LSP client behavior (extracted from the compiled binary) against Serena's Roslyn implementation, which has a fully working Roslyn integration.

1. Missing client capabilities

Claude Code declares:

capabilities: {
  workspace: { configuration: false, workspaceFolders: false },
  textDocument: {
    hover: {}, definition: {}, references: {},
    documentSymbol: { hierarchicalDocumentSymbolSupport: true },
    callHierarchy: {},
    // Missing: implementation, typeDefinition, signatureHelp
  },
  // Missing: workspace.symbol
}

Roslyn only enables server-side features when the client declares support for them. The missing textDocument.implementation capability is why goToImplementation fails silently.

Related: #32502

2. No Roslyn-specific solution/project notifications

After initialization, Roslyn expects solution/open and project/open notifications to know which solution and projects to load. Without these, it relies on --autoLoadProjects, which is less reliable and slower.

Serena sends these explicitly:

self.server.notify.send_notification("solution/open", {"solution": solution_uri})
self.server.notify.send_notification("project/open", {"projects": project_uris})

Claude Code sends neither.

3. No readiness wait

Roslyn emits a workspace/projectInitializationComplete notification when indexing is done. Serena waits up to 30 seconds for this before considering the server ready. Claude Code doesn't wait for any readiness signal, so operations called early in a session often fail.

4. workspace/configuration returns null for everything

When Roslyn pulls configuration via workspace/configuration, Claude Code returns null for every item. Serena returns sensible defaults (analysis scope, member insertion location, etc.). While Roslyn tolerates null responses, returning proper defaults improves behavior.

Related: #16360, #33552

Proposed Solution

For the plugin system (generic, benefits all LSP servers)

  1. Add textDocument.implementation and workspace.symbol to client capabilities — these are standard LSP features (#32502)
  2. Use plugin settings to respond to workspace/configuration pull requests — currently settings are only sent via didChangeConfiguration push, not used for pull responses (#33552)

For Roslyn specifically (could be solved generically)

  1. Support post-initialization notifications in plugin config — a new field like postInitNotifications in .lsp.json would let plugins send custom notifications after initialized. This is generic enough to benefit other servers too:
{
  "roslyn": {
    "command": "roslyn-language-server.cmd",
    "postInitNotifications": [
      { "method": "solution/open", "params": { "solution": "${WORKSPACE_SOLUTION_URI}" } }
    ]
  }
}
  1. Support a readiness notification in plugin config — a new field like readyNotification that tells the LSP client to wait for a specific notification before marking the server as ready:
{
  "roslyn": {
    "readyNotification": "workspace/projectInitializationComplete"
  }
}

Related Issues

  • #32502 — Expand LSP tool to support more protocol methods
  • #32595 — LSP client does not respond to client/registerCapability
  • #33552 — settings field not sent via workspace/didChangeConfiguration
  • #38198 — workspaceSymbol missing query parameter
  • #16360 — C# LSP missing workspace/configuration and other request handlers

Additional Context

  • Roslyn version tested: 5.5.0-2.26103.6 (installed as dotnet global tool roslyn-language-server)
  • Plugin config: custom local plugin with --stdio --autoLoadProjects args
  • Project size: ~60 projects in a .slnx solution (large WPF application)
  • Platform: Windows 11, Claude Code latest
  • Reference implementation: Serena's CSharpLanguageServer demonstrates a fully working Roslyn integration with all the above features

View original on GitHub ↗

This issue has 6 comments on GitHub. Read the full discussion on GitHub ↗