[BUG] LSP workspaceSymbol Implementation Violates LSP Specification

Resolved 💬 2 comments Opened Jan 29, 2026 by shridhardamle Closed Feb 28, 2026

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?

Claude Code's LSP tool incorrectly implements the Microsoft Language Server Protocol specification for multiple operations. The tool forces a uniform interface (filePath, line, character) for ALL 9 LSP operations, but the LSP specification defines different parameter types for different operations.

Violations Found (4 out of 9 operations):

CRITICAL - Operation 5: workspaceSymbol

  • Requires: filePath, line, character (all wrong)
  • Missing: query parameter (the only one that should exist)
  • LSP Spec: Should accept ONLY query: string to search symbols by name
  • Impact: Cannot search for symbols across workspace by name - primary LSP use case is completely broken

HIGH - Operations 8-9: incomingCalls / outgoingCalls

  • Requires: filePath, line, character (wrong type)
  • Should require: item: CallHierarchyItem (object from prepareCallHierarchy)
  • LSP Spec: Should accept the CallHierarchyItem returned by prepareCallHierarchy
  • Impact: Cannot properly chain call hierarchy operations per spec

MEDIUM - Operation 4: documentSymbol

  • Requires: filePath, line, character
  • Should require: textDocument (URI) only, no position
  • LSP Spec: Position parameters not required for document-level symbol listing
  • Impact: Operation works but forces unnecessary parameters

MEDIUM - Operation 2: findReferences

  • Missing: context.includeDeclaration parameter
  • LSP Spec: Should include optional context for including/excluding declarations
  • Impact: Cannot control whether to include symbol declarations in results

Working Correctly (5 out of 9 operations):

Operations 1, 3, 6, 7 (goToDefinition, hover, goToImplementation, prepareCallHierarchy) correctly map to LSP's TextDocumentPositionParams pattern.

What Should Happen?

Each LSP operation should follow the Microsoft Language Server Protocol specification exactly:

Operation 1: goToDefinition (textDocument/definition)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition

interface TextDocumentPositionParams {
  textDocument: TextDocumentIdentifier;  // { uri: string }
  position: Position;                     // { line: number, character: number }
}

Current: ✅ Works correctly (filePath maps to uri, line/character map to position)

Operation 2: findReferences (textDocument/references)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references

interface ReferenceParams extends TextDocumentPositionParams {
  context: ReferenceContext;  // { includeDeclaration: boolean }
}

Should accept: filePath, line, character, includeDeclaration (optional boolean)

Operation 3: hover (textDocument/hover)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover

interface HoverParams extends TextDocumentPositionParams {
  // Same as TextDocumentPositionParams
}

Current: ✅ Works correctly

Operation 4: documentSymbol (textDocument/documentSymbol)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol

interface DocumentSymbolParams {
  textDocument: TextDocumentIdentifier;  // { uri: string }
  // NO position required!
}

Should accept: filePath only (line/character should be optional)

Operation 5: workspaceSymbol (workspace/symbol)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol

interface WorkspaceSymbolParams {
  query: string;  // Search term for symbols
  // NO textDocument or position!
}

Should accept: query only (no filePath, line, or character)

Operation 6: goToImplementation (textDocument/implementation)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_implementation

interface ImplementationParams extends TextDocumentPositionParams {
  // Same as TextDocumentPositionParams
}

Current: ✅ Works correctly

Operation 7: prepareCallHierarchy (textDocument/prepareCallHierarchy)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareCallHierarchy

interface CallHierarchyPrepareParams extends TextDocumentPositionParams {
  // Same as TextDocumentPositionParams
}

Current: ✅ Works correctly

Operation 8: incomingCalls (callHierarchy/incomingCalls)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_incomingCalls

interface CallHierarchyIncomingCallsParams {
  item: CallHierarchyItem;  // NOT a document position!
}

Should accept: item (CallHierarchyItem object from prepareCallHierarchy)

Operation 9: outgoingCalls (callHierarchy/outgoingCalls)

LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_outgoingCalls

interface CallHierarchyOutgoingCallsParams {
  item: CallHierarchyItem;  // NOT a document position!
}

Should accept: item (CallHierarchyItem object from prepareCallHierarchy)

Error Messages/Logs

### workspaceSymbol Tests (Operation 5)


# Test 1: Omit filePath (should work per spec with just query)
LSP.workspaceSymbol(operation: "workspaceSymbol", line: 1, character: 1)
# Error:
InputValidationError: LSP failed due to the following issue:
The required parameter `filePath` is missing

# Test 2: Use directory path (workspace-level search)
LSP.workspaceSymbol(filePath: "/path/to/project", line: 1, character: 1)
# Error:
Path is not a file: /path/to/project

# Test 3: Use wildcard pattern
LSP.workspaceSymbol(filePath: "/path/to/project/*.ts", line: 1, character: 1)
# Error:
File does not exist: /path/to/project/*.ts

# Test 4: Use recursive glob
LSP.workspaceSymbol(filePath: "/path/to/project/**/*.ts", line: 1, character: 1)
# Error:
File does not exist: /path/to/project/**/*.ts

# Test 5: Use search term as filePath
LSP.workspaceSymbol(filePath: "myFunction", line: 1, character: 1)
# Error:
File does not exist: myFunction

# Test 6: Use valid file (only way that works)
LSP.workspaceSymbol(filePath: "/path/to/file.ts", line: 1, character: 1)
# Result:
Found 2 symbols in workspace:
  functionA (Function) - Line 5
  variableB (Constant) - Line 10
# Problem: Returns ALL symbols unfiltered, no way to search for specific symbol


### documentSymbol Tests (Operation 4)


# Test 1: With position (currently required)
LSP.documentSymbol(filePath: "/path/to/file.ts", line: 1, character: 1)
# Result:
Document symbols:
greet (Function) - Line 2
result (Constant) - Line 7
# Works but position shouldn't be required

# Test 2: Without position (should work per spec)
LSP.documentSymbol(filePath: "/path/to/file.ts")
# Error:
InputValidationError: LSP failed due to the following issue:
The required parameter `line` is missing


### All Operations: Wildcard/Directory Pattern Tests


# Tested patterns on ALL 9 operations:
# Pattern: *
Error: File does not exist: *

# Pattern: ./
Error: Path is not a file: ./

# Pattern: .
Error: Path is not a file: .

# Pattern: /path/to/workspace
Error: Path is not a file: /path/to/workspace

# Pattern: /path/to/workspace/*
Error: File does not exist: /path/to/workspace/*

# Pattern: /path/to/workspace/**/*
Error: File does not exist: /path/to/workspace/**/*

# Pattern: ~/workspace/**/*.ts
Error: File does not exist: ~/workspace/**/*.ts

# Conclusion: ALL operations reject directories and wildcards
# Only accept specific, existing file paths

Steps to Reproduce

Setup

  1. Install Claude Code v2.1.23 or later
  2. Install LSP plugins from official marketplace:

```bash
# Check available plugins
claude plugin search lsp

# Install TypeScript LSP
claude plugin install typescript-lsp@claude-plugins-official
```

  1. Install the actual language server binary:

``bash
npm install -g typescript-language-server typescript
``

  1. Verify LSP tool is available (restart Claude Code if needed)

Test 1: workspaceSymbol - Missing filePath

// Per LSP spec, should accept only query parameter
LSP.workspaceSymbol(
  operation: "workspaceSymbol",
  line: 1,
  character: 1
)

// Expected: Should accept query parameter to search by symbol name
// Actual: Error - "InputValidationError: The required parameter `filePath` is missing"

Test 2: workspaceSymbol - Directory Path

// Try workspace-level search with directory
LSP.workspaceSymbol(
  operation: "workspaceSymbol",
  filePath: "/Users/username/workspace",
  line: 1,
  character: 1
)

// Expected: Should search across workspace directory
// Actual: Error - "Path is not a file: /Users/username/workspace"

Test 3: workspaceSymbol - Wildcard Pattern

// Try with glob pattern
LSP.workspaceSymbol(
  operation: "workspaceSymbol",
  filePath: "/Users/username/workspace/**/*.ts",
  line: 1,
  character: 1
)

// Expected: Should search across matching files
// Actual: Error - "File does not exist: /Users/username/workspace/**/*.ts"

Test 4: workspaceSymbol - Search Term as Path

// Try using what should be a query as filePath
LSP.workspaceSymbol(
  operation: "workspaceSymbol",
  filePath: "authenticate",
  line: 1,
  character: 1
)

// Expected: "authenticate" should be treated as search query
// Actual: Error - "File does not exist: authenticate"

Test 5: workspaceSymbol - Valid File (Only Way That Works)

// Must provide specific file path
LSP.workspaceSymbol(
  operation: "workspaceSymbol",
  filePath: "/Users/username/workspace/src/file.ts",
  line: 1,
  character: 1
)

// Result: Returns ALL symbols unfiltered
// Problem: No way to specify which symbol to search for

Test 6: documentSymbol - Without Position

// Per LSP spec, position not required for documentSymbol
LSP.documentSymbol(
  operation: "documentSymbol",
  filePath: "/Users/username/workspace/src/file.ts"
)

// Expected: Should work with just file path
// Actual: Error - "InputValidationError: The required parameter `line` is missing"

Test 7: Verify All 9 Operations Require File Path

// Try each operation without filePath:
const operations = [
  "goToDefinition",
  "findReferences",
  "hover",
  "documentSymbol",
  "workspaceSymbol",
  "goToImplementation",
  "prepareCallHierarchy",
  "incomingCalls",
  "outgoingCalls"
];

operations.forEach(op => {
  LSP[op](operation: op, line: 1, character: 1);
  // All return: "InputValidationError: The required parameter `filePath` is missing"
});

Claude Model

Sonnet (default)

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.23

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Background: No Official LSP Tool Documentation

IMPORTANT: There is currently no official documentation for the LSP tool's parameters or usage. The official Claude Code team has acknowledged:

"Support for LSP in Claude Code is pretty raw still. There are bugs in the different LSP operations, no documentation, and no UI indication that your LSP servers are started/running/have errors or even exist."

What We Know from Actual Testing:

Through extensive testing, we've determined that Claude Code's LSP tool currently requires for ALL 9 operations:

  • filePath (string) - Must be an existing file path
  • line (number) - Position line number
  • character (number) - Position character offset

Validation behavior observed:

  • Rejects directories: Path is not a file: /path/to/dir
  • Rejects wildcards: File does not exist: *.ts
  • Requires exact file paths for ALL operations, including workspaceSymbol

The Core Issue:

The current implementation forces a uniform interface (file path + position) for ALL operations, which contradicts the Microsoft LSP specification. Different LSP operations are designed to accept different parameter types.

Related Official Information:

---

Detailed Comparison: LSP Spec vs Claude Code (All 9 Operations)

1. goToDefinition (textDocument/definition)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_definition

interface TextDocumentPositionParams {
  textDocument: TextDocumentIdentifier;  // { uri: string }
  position: Position;                     // { line: number, character: number }
}

Claude Code Implementation:

📋 Tested: Works correctly with file path and position

{
  operation: "goToDefinition",
  filePath: string,     // ✅ Correct (maps to textDocument.uri)
  line: number,         // ✅ Correct (maps to position.line)
  character: number     // ✅ Correct (maps to position.character)
}

Status: ✅ Correct (tested and working)

---

2. findReferences (textDocument/references)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_references

interface ReferenceParams extends TextDocumentPositionParams {
  context: ReferenceContext;  // { includeDeclaration: boolean }
}

Claude Code Implementation:

📋 Tested: Works correctly with file path and position

{
  operation: "findReferences",
  filePath: string,     // ✅ Correct (maps to textDocument.uri)
  line: number,         // ✅ Correct (maps to position.line)
  character: number     // ✅ Correct (maps to position.character)
  // ❌ MISSING: context parameter
}

Status: ⚠️ Incomplete (missing context.includeDeclaration option)

---

3. hover (textDocument/hover)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_hover

interface HoverParams extends TextDocumentPositionParams {
  // Same as TextDocumentPositionParams
}

Claude Code Implementation:

📋 Tested: Works correctly with file path and position

{
  operation: "hover",
  filePath: string,     // ✅ Correct (maps to textDocument.uri)
  line: number,         // ✅ Correct (maps to position.line)
  character: number     // ✅ Correct (maps to position.character)
}

Status: ✅ Correct

---

4. documentSymbol (textDocument/documentSymbol)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_documentSymbol

interface DocumentSymbolParams {
  textDocument: TextDocumentIdentifier;  // { uri: string }
  // NO position required!
}

Claude Code Implementation:

📋 Actual Test Results:

// Test 1: With position (currently required)
LSP.documentSymbol(filePath: "/path/to/file.ts", line: 1, character: 1)
// Result: Works, returns symbols: "greet (Function) - Line 2, result (Constant) - Line 7"

// Test 2: Without position (should work per spec)
LSP.documentSymbol(filePath: "/path/to/file.ts")
// Error: "InputValidationError: The required parameter `line` is missing"

Required parameters:

{
  operation: "documentSymbol",
  filePath: string,     // ✅ Correct (maps to textDocument.uri)
  line: number,         // ❌ NOT REQUIRED BY SPEC
  character: number     // ❌ NOT REQUIRED BY SPEC
}

Status:WRONG - Requires unnecessary line and character parameters

Impact: Minor (operation works but forces unnecessary parameters)

---

5. workspaceSymbol (workspace/symbol)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol

interface WorkspaceSymbolParams {
  query: string;  // Search term for symbols
  // NO textDocument or position!
}

Claude Code Implementation:

📋 Actual Test Results:

// Test 1: Omit filePath
LSP.workspaceSymbol(operation: "workspaceSymbol", line: 1, character: 1)
// Error: "InputValidationError: The required parameter `filePath` is missing"

// Test 2: Use directory path
LSP.workspaceSymbol(filePath: "/path/to/project", line: 1, character: 1)
// Error: "Path is not a file: /path/to/project"

// Test 3: Use wildcard
LSP.workspaceSymbol(filePath: "/path/to/project/*.ts", line: 1, character: 1)
// Error: "File does not exist: /path/to/project/*.ts"

// Test 4: Use search term as filePath
LSP.workspaceSymbol(filePath: "myFunction", line: 1, character: 1)
// Error: "File does not exist: myFunction"

// Test 5: Use valid file (only way that works)
LSP.workspaceSymbol(filePath: "/path/to/file.ts", line: 1, character: 1)
// Result: Returns ALL symbols unfiltered, no way to search for specific symbol

Required parameters:

{
  operation: "workspaceSymbol",
  filePath: string,     // ❌ NOT IN SPEC
  line: number,         // ❌ NOT IN SPEC
  character: number     // ❌ NOT IN SPEC
  // ❌ MISSING: query parameter (the only one that should exist!)
}

Status:COMPLETELY WRONG - Requires wrong parameters, missing required parameter

Impact: CRITICAL - Operation is completely unusable for its intended purpose

---

6. goToImplementation (textDocument/implementation)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_implementation

interface ImplementationParams extends TextDocumentPositionParams {
  // Same as TextDocumentPositionParams
}

Claude Code Implementation:

📋 Tested: Works correctly with file path and position

{
  operation: "goToImplementation",
  filePath: string,     // ✅ Correct (maps to textDocument.uri)
  line: number,         // ✅ Correct (maps to position.line)
  character: number     // ✅ Correct (maps to position.character)
}

Status: ✅ Correct

---

7. prepareCallHierarchy (textDocument/prepareCallHierarchy)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_prepareCallHierarchy

interface CallHierarchyPrepareParams extends TextDocumentPositionParams {
  // Same as TextDocumentPositionParams
}

Claude Code Implementation:

📋 Tested: Works correctly with file path and position

{
  operation: "prepareCallHierarchy",
  filePath: string,     // ✅ Correct (maps to textDocument.uri)
  line: number,         // ✅ Correct (maps to position.line)
  character: number     // ✅ Correct (maps to position.character)
}

Status: ✅ Correct

---

8. incomingCalls (callHierarchy/incomingCalls)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_incomingCalls

interface CallHierarchyIncomingCallsParams {
  item: CallHierarchyItem;  // NOT a document position!
}

interface CallHierarchyItem {
  name: string;
  kind: SymbolKind;
  uri: DocumentUri;
  range: Range;
  selectionRange: Range;
  // ... other fields
}

Claude Code Implementation:

📋 Tested: Works correctly with file path and position

{
  operation: "incomingCalls",
  filePath: string,     // ❌ WRONG TYPE (should be CallHierarchyItem)
  line: number,         // ❌ WRONG TYPE (should be CallHierarchyItem)
  character: number     // ❌ WRONG TYPE (should be CallHierarchyItem)
}

Status:WRONG - Should accept CallHierarchyItem, not file position

Impact: HIGH - Operation may work by accident but doesn't follow spec

---

9. outgoingCalls (callHierarchy/outgoingCalls)

LSP Specification:

📖 Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_outgoingCalls

interface CallHierarchyOutgoingCallsParams {
  item: CallHierarchyItem;  // NOT a document position!
}

Claude Code Implementation:

📋 Tested: Works correctly with file path and position

{
  operation: "outgoingCalls",
  filePath: string,     // ❌ WRONG TYPE (should be CallHierarchyItem)
  line: number,         // ❌ WRONG TYPE (should be CallHierarchyItem)
  character: number     // ❌ WRONG TYPE (should be CallHierarchyItem)
}

Status:WRONG - Should accept CallHierarchyItem, not file position

Impact: HIGH - Operation may work by accident but doesn't follow spec

---

Summary Table

| Operation | LSP Spec Parameters | Claude Code Parameters | Status |
|-----------|---------------------|------------------------|--------|
| 1. goToDefinition | textDocument, position | filePath, line, character | ✅ Correct |
| 2. findReferences | textDocument, position, context | filePath, line, character | ⚠️ Missing context |
| 3. hover | textDocument, position | filePath, line, character | ✅ Correct |
| 4. documentSymbol | textDocument only | filePath, line, character | ❌ Extra params |
| 5. workspaceSymbol | query only | filePath, line, character | ❌ WRONG |
| 6. goToImplementation | textDocument, position | filePath, line, character | ✅ Correct |
| 7. prepareCallHierarchy | textDocument, position | filePath, line, character | ✅ Correct |
| 8. incomingCalls | item (CallHierarchyItem) | filePath, line, character | ❌ WRONG |
| 9. outgoingCalls | item (CallHierarchyItem) | filePath, line, character | ❌ WRONG |

Issues Found: 4 operations with violations

---

Proof: VS Code's Implementation (Microsoft Reference Implementation)

Microsoft's VS Code correctly implements the LSP specification. Here's proof from their actual source code:

Repository: https://github.com/microsoft/vscode-languageserver-node (Official Microsoft LSP implementation for VS Code)

---

Operations 1-3, 6-7: TextDocumentPositionParams

VS Code Implementation:

📖 Source: https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/src/common/protocol.ts

interface TextDocumentPositionParams {
  textDocument: TextDocumentIdentifier;  // { uri: string }
  position: Position;                     // { line: number, character: number }
}

Uses: textDocument (URI) + position (line, character)
Matches LSP Spec: Exactly
Claude Code: Matches (uses filePath/line/character)

---

Operation 4: documentSymbol - ONLY textDocument, NO position

VS Code Implementation:

📖 Source: https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/src/node/test/connection.test.ts

const params: DocumentSymbolParams = {
  textDocument: { uri: 'file:///abc.txt' }
  // NO position parameter!
};

Uses: ONLY textDocument (URI)
Matches LSP Spec: Exactly
Claude Code: Forces unnecessary line and character parameters

---

Operation 5: workspaceSymbol - ONLY query, NO file/position

VS Code Implementation:

📖 Source: https://github.com/microsoft/vscode-cpptools/blob/main/Extension/src/LanguageServer/Providers/workspaceSymbolProvider.ts

const params: WorkspaceSymbolParams = {
  query: query  // ONLY query string!
};

const symbols: LocalizeSymbolInformation[] =
  await this.client.languageClient.sendRequest(GetSymbolInfoRequest, params, token);

Uses: ONLY query (search string)
Matches LSP Spec: Exactly
Claude Code: Requires filePath, line, character - missing query entirely

---

Operations 8-9: incomingCalls/outgoingCalls - item (CallHierarchyItem), NOT file/position

VS Code Implementation:

📖 Source: https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/src/common/protocol.callHierarchy.ts

export interface CallHierarchyIncomingCallsParams
  extends WorkDoneProgressParams, PartialResultParams {
  item: CallHierarchyItem;  // NOT a file position!
}

export interface CallHierarchyOutgoingCallsParams
  extends WorkDoneProgressParams, PartialResultParams {
  item: CallHierarchyItem;  // NOT a file position!
}

Uses: item (CallHierarchyItem object from prepareCallHierarchy)
Matches LSP Spec: Exactly
Claude Code: Requires filePath, line, character instead of CallHierarchyItem

---

Proof: Neovim's Implementation

Neovim also correctly implements the LSP specification:

Repository: https://github.com/neovim/neovim

Operation 5: workspaceSymbol

Neovim Implementation:

📖 Source: https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/buf.lua

function M.workspace_symbol(query, opts)
  local params = { query = query }  -- ONLY query string!
  request_with_opts('workspace/symbol', params, opts)
end

Handler:

📖 Source: https://github.com/neovim/neovim/blob/master/runtime/lua/vim/lsp/handlers.lua

RCS['workspace/symbol'] = response_to_list(util.symbols_to_items, 'symbols', function(ctx)
  return string.format("Symbols matching '%s'", ctx.params.query)  -- Uses query from params
end)

Uses: ONLY query (search string)
Matches LSP Spec: Exactly
Claude Code: Requires filePath, line, character - missing query entirely

---

Summary: VS Code & Neovim vs Claude Code

| Operation | VS Code/Neovim Parameters | Claude Code Parameters | Match? |
|-----------|---------------------------|------------------------|--------|
| 1-3, 6-7 | textDocument, position | filePath, line, character | ✅ Equivalent |
| 4 (documentSymbol) | textDocument only | filePath, line, character | ❌ Extra params |
| 5 (workspaceSymbol) | query only | filePath, line, character | ❌ WRONG |
| 8-9 (incoming/outgoingCalls) | item (CallHierarchyItem) | filePath, line, character | ❌ WRONG |

Conclusion: Both Microsoft's VS Code and Neovim (the two major LSP reference implementations) prove Claude Code's violations of the LSP specification.

---

Impact Analysis

High Severity Issues

1. workspaceSymbol (CRITICAL)

  • ❌ Cannot search for symbols by name across workspace
  • ❌ Primary use case is completely broken
  • ❌ Forces fallback to text-based grep instead of semantic search
  • ❌ Defeats the core value proposition of LSP integration

2. incomingCalls / outgoingCalls (HIGH)

  • ❌ Wrong parameter type (should be CallHierarchyItem)
  • ⚠️ May work by accident in simple cases
  • ❌ Cannot be used in complex scenarios requiring CallHierarchyItem data

Medium Severity Issues

3. documentSymbol (MEDIUM)

  • ⚠️ Requires unnecessary position parameters
  • ✅ Still functions correctly despite extra parameters

4. findReferences (MEDIUM)

  • ⚠️ Missing context.includeDeclaration option
  • ✅ Works for basic use cases

---

Use Case Examples

Example 1: Finding a function across workspace

User request: "Find the authenticate function in my project"

What should work (per LSP spec):

LSP.workspaceSymbol(query: "authenticate")
// Returns: Symbol locations across entire workspace

What actually happens:

Error: The required parameter `filePath` is missing
Workaround: Must use grep/text search instead

---

Example 2: Getting call hierarchy

User request: "Show me what calls this function"

What should work (per LSP spec):

// Step 1: Prepare call hierarchy at a position
const item = LSP.prepareCallHierarchy(
  filePath: "file.ts",
  line: 10,
  character: 5
)
// Returns: CallHierarchyItem

// Step 2: Get incoming calls using that item
LSP.incomingCalls(item: item)
// Should accept the CallHierarchyItem from step 1

What Claude Code forces:

// Step 1: Works correctly
const item = LSP.prepareCallHierarchy(
  filePath: "file.ts",
  line: 10,
  character: 5
)
// Returns: "Call hierarchy item: greet (Function) - file.ts:1"

// Step 2: Cannot use the item
LSP.incomingCalls(filePath: "file.ts", line: 10, character: 5)
// Must provide position again, cannot pass CallHierarchyItem
// Works by accident but doesn't follow spec

---

Expected Behavior Per LSP Specification

Example 1: Workspace Symbol Search

Per LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#workspace_symbol

// What should work:
LSP.workspaceSymbol(query: "authenticate")

// Should return symbols matching "authenticate" from across the workspace
// Response: Array of SymbolInformation with locations

Current Claude Code Behavior (tested):

// What's currently required:
LSP.workspaceSymbol(filePath: "/path/to/file.ts", line: 1, character: 1)

// Returns: ALL symbols unfiltered, no way to search by name
// Cannot specify what symbol to search for

---

Example 2: Call Hierarchy

Per LSP Spec: https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#callHierarchy_incomingCalls

// Step 1: Prepare call hierarchy at a position
const item = LSP.prepareCallHierarchy(
  filePath: "file.ts",
  line: 10,
  character: 5
)
// Returns: CallHierarchyItem

// Step 2: Get incoming calls using that item
LSP.incomingCalls(item: item)
// Should accept the CallHierarchyItem from step 1

Current Claude Code Behavior (tested):

// Step 1: Works correctly
const item = LSP.prepareCallHierarchy(
  filePath: "file.ts",
  line: 10,
  character: 5
)
// Returns: "Call hierarchy item: greet (Function) - test-lsp.ts:1"

// Step 2: Cannot use the item
LSP.incomingCalls(filePath: "file.ts", line: 10, character: 5)
// Must provide position again, cannot pass CallHierarchyItem
// Works by accident but doesn't follow spec

---

Suggested Fixes

1. workspaceSymbol (CRITICAL)

Remove:

  • filePath requirement
  • line requirement
  • character requirement

Add:

  • query parameter (required string)

Example:

{
  operation: "workspaceSymbol",
  query: "authenticate"  // NEW: The symbol name to search for
}

---

2. incomingCalls / outgoingCalls (HIGH)

Change parameters to accept CallHierarchyItem:

Option A: Accept CallHierarchyItem directly

{
  operation: "incomingCalls",
  item: CallHierarchyItem  // From prepareCallHierarchy result
}

Option B: Keep current interface but auto-resolve

{
  operation: "incomingCalls",
  filePath: string,
  line: number,
  character: number
}
// Internally: call prepareCallHierarchy first, then incomingCalls

---

3. documentSymbol (MEDIUM)

Make position parameters optional:

{
  operation: "documentSymbol",
  filePath: string,
  line?: number,        // OPTIONAL
  character?: number    // OPTIONAL
}

---

4. findReferences (MEDIUM)

Add context parameter:

{
  operation: "findReferences",
  filePath: string,
  line: number,
  character: number,
  includeDeclaration?: boolean  // NEW: Default to true
}

---

References

Claude Code Official Resources

Microsoft LSP Specification Documentation

Specific Operations

Related Issues

  • #14803 - LSP race condition (fixed in v2.1.0+)
  • #17312 - LSP empty results on Windows

---

Priority

CRITICAL: workspaceSymbol - Makes workspace-level symbol discovery impossible

HIGH: incomingCalls/outgoingCalls - Wrong parameter types violate spec

MEDIUM: documentSymbol, findReferences - Minor spec violations, operations still functional

---

Root Cause Analysis

The Problem: Claude Code's LSP Tool implementation forces a uniform interface (filePath, line, character) for ALL 9 LSP operations. This "one size fits all" approach violates the Microsoft LSP specification, which defines different parameter structures for different operations.

Why This Matters:

The LSP specification was designed with different parameter types for good reasons:

  • Document-level operations (documentSymbol) don't need a position - they scan the entire document
  • Workspace-level operations (workspaceSymbol) don't need a file - they search across all files
  • Hierarchy operations (incomingCalls/outgoingCalls) need the result object from a previous operation - not a file position

By forcing all operations to use file + position, Claude Code:

  1. Makes workspace-level search impossible (no way to specify search query)
  2. Breaks operation chaining (can't pass CallHierarchyItem between operations)
  3. Forces unnecessary parameters (documentSymbol doesn't need position)

The Fix: Align each LSP operation with its specification-defined parameter structure.

---

Note: This issue affects the core value proposition of LSP integration. Users expect LSP to provide semantic code intelligence (understanding symbols, types, and relationships), but the current implementation forces users to fall back to text-based search tools (grep) for workspace-level operations.

View original on GitHub ↗

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