LSP Setup Skill
This skill guides you through installing and configuring LSP servers for GitHub Copilot CLI. It supports Linux and macOS and covers 5 language servers.
General Guidelines
OS Detection
Always detect the operating system first:
OS="$(uname -s)"
Linux→ useapt(Debian/Ubuntu) or check for other package managersDarwin→ usebrew
Path Conventions
- Never use hardcoded absolute paths like
/home/username/... - Always use
$HOMEfor home directory references - Resolve binary paths dynamically with
command -v <binary>orwhich <binary> - The LSP config file is located at
$HOME/.copilot/lsp-config.json
Workflow
For each LSP server:
- Check if it's already installed (
command -v <binary>) - Install it using the appropriate method for the detected OS
- Resolve the absolute path of the installed binary
- Add an entry to
$HOME/.copilot/lsp-config.json - Verify with
/lsp showand/lsp test <name>
LSP Server: Go (gopls)
Prerequisites
- Go must be installed (
go version)
Install
go install golang.org/x/tools/gopls@latest
Works identically on Linux and macOS. The binary is installed to $HOME/go/bin/gopls (or $GOPATH/bin/gopls if GOPATH is set).
Verify
GOPLS_PATH="$(command -v gopls || echo "$HOME/go/bin/gopls")"
"$GOPLS_PATH" version
lsp-config.json entry
"go": {
"command": "<resolved absolute path to gopls>",
"args": ["serve"],
"fileExtensions": {
".go": "go"
}
}
LSP Server: TypeScript/JavaScript (typescript-language-server)
Prerequisites
- Node.js must be installed (
node --version)
Install
npm install -g typescript-language-server typescript
Works identically on Linux and macOS.
Verify
TSL_PATH="$(command -v typescript-language-server)"
"$TSL_PATH" --version
lsp-config.json entry
"typescript": {
"command": "<resolved absolute path to typescript-language-server>",
"args": ["--stdio"],
"fileExtensions": {
".ts": "typescript",
".tsx": "typescriptreact",
".js": "javascript",
".jsx": "javascriptreact"
}
}
LSP Server: Python (pylsp)
Prerequisites
- Python 3 must be installed (
python3 --version) - pip3 must be available (
pip3 --version)
Install
Detect if --break-system-packages is needed (required on Debian/Ubuntu with PEP 668 externally-managed Python):
OS="$(uname -s)"
if [ "$OS" = "Linux" ] && python3 -c "import sysconfig; print(sysconfig.get_path('stdlib'))" 2>/dev/null | grep -q "/usr/lib"; then
pip3 install --user --break-system-packages python-lsp-server
else
pip3 install --user python-lsp-server
fi
On macOS with Homebrew Python, --user install works without --break-system-packages.
Verify
PYLSP_PATH="$(command -v pylsp || echo "$HOME/.local/bin/pylsp")"
"$PYLSP_PATH" --help
lsp-config.json entry
"python": {
"command": "<resolved absolute path to pylsp>",
"args": [],
"fileExtensions": {
".py": "python"
}
}
LSP Server: C/C++ (clangd)
Prerequisites
- None (installed via system package manager)
Install
OS="$(uname -s)"
if [ "$OS" = "Darwin" ]; then
# clangd is included with Xcode Command Line Tools or can be installed via brew
if ! command -v clangd &>/dev/null; then
brew install llvm
# clangd will be at $(brew --prefix llvm)/bin/clangd
fi
elif [ "$OS" = "Linux" ]; then
sudo apt install -y clangd
fi
Verify
CLANGD_PATH="$(command -v clangd)"
# On macOS with brew llvm, it may be at: $(brew --prefix llvm)/bin/clangd
if [ -z "$CLANGD_PATH" ] && [ "$(uname -s)" = "Darwin" ]; then
CLANGD_PATH="$(brew --prefix llvm)/bin/clangd"
fi
"$CLANGD_PATH" --version
lsp-config.json entry
"cpp": {
"command": "<resolved absolute path to clangd>",
"args": [],
"fileExtensions": {
".c": "c",
".h": "c",
".cpp": "cpp",
".hpp": "cpp",
".cc": "cpp",
".cxx": "cpp"
}
}
LSP Server: Java (jdtls)
Prerequisites
- Java 21+ must be installed (
java -version)
Install
- Download the latest jdtls release:
JDTLS_VERSION="1.56.0"
JDTLS_BUILD="202601291528"
JDTLS_DIR="$HOME/.local/share/jdtls"
mkdir -p "$JDTLS_DIR"
curl -sL "https://www.eclipse.org/downloads/download.php?file=/jdtls/milestones/${JDTLS_VERSION}/jdt-language-server-${JDTLS_VERSION}-${JDTLS_BUILD}.tar.gz&r=1" \
-o /tmp/jdtls.tar.gz -L
tar -xzf /tmp/jdtls.tar.gz -C "$JDTLS_DIR"
rm -f /tmp/jdtls.tar.gz
- Create a cross-platform wrapper script:
mkdir -p "$HOME/.local/bin"
cat > "$HOME/.local/bin/jdtls" << 'WRAPPER'
#!/bin/sh
# Cross-platform JAVA_HOME detection
if [ -z "$JAVA_HOME" ]; then
OS="$(uname -s)"
if [ "$OS" = "Darwin" ]; then
JAVA_HOME="$(/usr/libexec/java_home 2>/dev/null || true)"
elif [ "$OS" = "Linux" ]; then
JAVA_BIN="$(readlink -f "$(command -v java)" 2>/dev/null)"
if [ -n "$JAVA_BIN" ]; then
JAVA_HOME="$(dirname "$(dirname "$JAVA_BIN")")"
fi
fi
export JAVA_HOME
fi
exec "$HOME/.local/share/jdtls/bin/jdtls" "$@"
WRAPPER
chmod +x "$HOME/.local/bin/jdtls"
Verify
"$HOME/.local/bin/jdtls" --help
lsp-config.json entry
"java": {
"command": "<HOME>/.local/bin/jdtls",
"args": [],
"fileExtensions": {
".java": "java"
}
}
Replace <HOME> with the resolved $HOME path.
Generating the Full lsp-config.json
After installing the desired LSP servers, generate or update $HOME/.copilot/lsp-config.json.
The final file should look like this (include only the servers that were installed):
{
"lspServers": {
"go": {
"command": "<absolute path to gopls>",
"args": ["serve"],
"fileExtensions": { ".go": "go" }
},
"typescript": {
"command": "<absolute path to typescript-language-server>",
"args": ["--stdio"],
"fileExtensions": {
".ts": "typescript",
".tsx": "typescriptreact",
".js": "javascript",
".jsx": "javascriptreact"
}
},
"python": {
"command": "<absolute path to pylsp>",
"args": [],
"fileExtensions": { ".py": "python" }
},
"cpp": {
"command": "<absolute path to clangd>",
"args": [],
"fileExtensions": {
".c": "c", ".h": "c",
".cpp": "cpp", ".hpp": "cpp",
".cc": "cpp", ".cxx": "cpp"
}
},
"java": {
"command": "<absolute path to jdtls>",
"args": [],
"fileExtensions": { ".java": "java" }
}
}
}
Important: Always resolve paths dynamically using command -v or known install locations. Never hardcode user-specific paths.
Verification
After updating the config, instruct the user to run in Copilot CLI:
/lsp reload
/lsp show
To test a specific server:
/lsp test go
/lsp test typescript
/lsp test python
/lsp test cpp
/lsp test java