LSP plugin: typescript-language-server fails on Windows (ENOENT for .cmd shims)
Description
The TypeScript LSP plugin (typescript-lsp@claude-plugins-official) fails on Windows because npm install -g typescript-language-server creates .cmd shims, not real .exe binaries. Claude Code's LSP spawner uses Node.js spawn() (libuv uv_spawn) without shell: true, which cannot execute .cmd files on Windows.
Error
Error performing documentSymbol: ENOENT: no such file or directory, uv_spawn 'typescript-language-server'
Environment
- Windows Server 2022 (10.0.20348)
- Node.js v22.14.0
- typescript-language-server 5.1.3 (globally installed via npm)
- Claude Code CLI (native .exe)
Reproduction
// This fails (what Claude Code does):
const { spawn } = require('child_process');
spawn('typescript-language-server', ['--version']);
// → ENOENT
// This works:
spawn('typescript-language-server', ['--version'], { shell: true });
// → 5.1.3
The binary IS on PATH — which typescript-language-server finds it, and execSync (which uses a shell) works. Only shellless spawn fails because it can't resolve .cmd shims.
Suggested Fix
In the LSP process spawner, either:
- Add
shell: trueto spawn options on Windows - Use
cross-spawnwhich handles this transparently - Detect Windows and append
.cmdto the command name
This likely affects all LSP plugins on Windows, not just TypeScript.
Workaround Attempted
Compiled a C# .exe shim that delegates to cmd.exe /c typescript-language-server.cmd. The binary is found, but stdio handle inheritance through the shim doesn't work for LSP's bidirectional JSON-RPC protocol.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗