OpenCode custom tools that expose LSP capabilities to AI agents.
This project provides OpenCode custom tools that give AI agents the same language-aware capabilities an IDE has. The tools act as LSP (Language Server Protocol) clients — they spawn existing language servers (like gopls, typescript-language-server, ty, rust-analyzer) as child processes and communicate via stdio JSON-RPC.
With these tools, an agent can:
- Jump to symbol definitions across a codebase
- Find all references to a variable, function, or type
- Get type information and documentation on hover
- Rename symbols safely across an entire workspace
- List and execute code actions (quick fixes, refactorings)
The tools use a warm server pool architecture for performance. Language servers are spawned on first use and kept alive for 5 minutes after the last request. During that window, subsequent requests reuse the same server instance — no cold-start delay. Each server is keyed by (language server ID, workspace root), so different projects get isolated servers.
When the idle timeout expires, the server is gracefully shut down to free resources.
| Language | Language Server | Install |
|---|---|---|
| Go | gopls | go install golang.org/x/tools/gopls@latest |
| TypeScript / JavaScript | typescript-language-server | npm install -g typescript-language-server typescript |
| Python | ty | pip install ty |
| Rust | rust-analyzer | rustup component add rust-analyzer |
-
Copy (or symlink) the
.opencode/directory into your project root, or place it in~/.config/opencode/for global use. -
Run
npm installin theagentic-lspdirectory to install dependencies:cd path/to/agentic-lsp npm install -
Install the language servers you need (see table above).
Purpose: Find where a symbol is defined.
| Argument | Type | Required | Description |
|---|---|---|---|
file |
string | Yes | Absolute path to the file containing the symbol |
line |
number | Yes | Zero-based line number |
character |
number | Yes | Zero-based character offset on the line |
Example:
lsp_definition(file="/home/user/project/src/main.go", line=42, character=15)
→ /home/user/project/src/handler.go:10:5
Purpose: Find all references to a symbol across the workspace.
| Argument | Type | Required | Description |
|---|---|---|---|
file |
string | Yes | Absolute path to the file containing the symbol |
line |
number | Yes | Zero-based line number |
character |
number | Yes | Zero-based character offset on the line |
includeDeclaration |
boolean | No | Include the symbol's declaration in results (default: true) |
Example:
lsp_references(file="/home/user/project/src/utils.ts", line=5, character=16)
→ Found 3 references:
/home/user/project/src/utils.ts:6:17
/home/user/project/src/index.ts:12:8
/home/user/project/src/tests/utils.test.ts:8:22
Purpose: Get type information and documentation for a symbol.
| Argument | Type | Required | Description |
|---|---|---|---|
file |
string | Yes | Absolute path to the file containing the symbol |
line |
number | Yes | Zero-based line number |
character |
number | Yes | Zero-based character offset on the line |
Example:
lsp_hover(file="/home/user/project/src/server.ts", line=20, character=10)
→ (method) http.Server.listen(port: number, callback?: () => void): http.Server
Start a server listening for connections.
Purpose: Rename a symbol across the entire workspace.
| Argument | Type | Required | Description |
|---|---|---|---|
file |
string | Yes | Absolute path to the file containing the symbol to rename |
line |
number | Yes | Zero-based line number |
character |
number | Yes | Zero-based character offset on the line |
newName |
string | Yes | The new name for the symbol |
apply |
boolean | No | If true, apply changes to disk; if false (default), preview only |
Example (preview):
lsp_rename(file="/home/user/project/src/api.ts", line=8, character=9, newName="fetchUserData")
→ Rename preview - files affected:
src/api.ts: 2 edits
src/routes/users.ts: 4 edits
src/tests/api.test.ts: 3 edits
Use apply=true to apply these changes.
Example (apply):
lsp_rename(file="/home/user/project/src/api.ts", line=8, character=9, newName="fetchUserData", apply=true)
→ Rename applied successfully. Modified files:
- src/api.ts (2 edits)
- src/routes/users.ts (4 edits)
- src/tests/api.test.ts (3 edits)
Purpose: Get available code actions (quick fixes, refactorings) for a range.
| Argument | Type | Required | Description |
|---|---|---|---|
file |
string | Yes | Absolute path to the file |
startLine |
number | Yes | Zero-based start line of the range |
startCharacter |
number | Yes | Zero-based start character of the range |
endLine |
number | Yes | Zero-based end line of the range |
endCharacter |
number | Yes | Zero-based end character of the range |
execute |
number | No | If provided, execute the action at this index (1-indexed) |
Example (list actions):
lsp_codeactions(file="/home/user/project/src/main.go", startLine=15, startCharacter=0, endLine=15, endCharacter=50)
→ Available code actions:
1. [quickfix] Organize imports
2. [refactor.extract] Extract function
3. [refactor.inline] Inline variable
Example (execute action):
lsp_codeactions(file="/home/user/project/src/main.go", startLine=15, startCharacter=0, endLine=15, endCharacter=50, execute=1)
→ Executed "Organize imports". Modified files:
- src/main.go
.opencode/tools/
├── lib/
│ ├── types.ts # Shared LSP types
│ ├── language-config.ts # Language server spawn configs
│ ├── lsp-client.ts # Core LSP JSON-RPC client
│ └── server-pool.ts # Warm server pool (5 min idle timeout)
├── lsp_definition.ts # Go to Definition
├── lsp_references.ts # Find References
├── lsp_hover.ts # Hover / type info
├── lsp_rename.ts # Rename symbol
└── lsp_codeactions.ts # Code actions / quick fixes
-
types.ts — Shared TypeScript type definitions for LSP positions, ranges, locations, text edits, code actions, and diagnostics.
-
language-config.ts — Configuration for each supported language server, including the command to spawn it, supported file extensions, and LSP language ID.
-
lsp-client.ts — The core LSP client that spawns a language server process, establishes a JSON-RPC connection over stdio, sends the LSP
initializehandshake, and provides methods for opening documents and sending requests. -
server-pool.ts — Manages a pool of warm language server instances. Servers are reused for subsequent requests to the same workspace and automatically shut down after 5 minutes of inactivity.
All line and character arguments are zero-based, as per the LSP specification:
- Line 0 is the first line of the file
- Character 0 is the first character on a line
Output locations (in tool results) are formatted as 1-based for human readability (e.g., file.ts:10:5 means line 10, column 5).