Code intelligence with language servers
Let the agent see real compiler diagnostics instead of guessing, and learn the cases where the language server misleads it.
Automatic detection
OpenCode starts a language server for the languages it recognises in your project and feeds the diagnostics back to the model. The practical effect is large: instead of inferring that a rename broke three call sites, the agent reads the actual type error and fixes it in the same turn.
# check which servers are running in a session
/lsp
# the same information appears in the log output
opencode run --print-logs "add a required field to the Order type and fix the fallout"- Servers are detected from file extensions and project markers such as package.json, go.mod or Cargo.toml.
- A server must be installed on the machine - OpenCode orchestrates it, it does not bundle a compiler.
- Diagnostics are cheaper than a test run, so they give the agent a fast feedback signal on every edit.
| Language | Server | Gives the agent |
|---|---|---|
| TypeScript / JavaScript | typescript-language-server | Type errors, unused imports |
| Python | pyright or basedpyright | Type and import errors |
| Go | gopls | Compile errors, unused variables |
| Rust | rust-analyzer | Borrow-check and type errors |
| Java | jdtls | Compiler diagnostics |
Per-language configuration
{
"$schema": "https://opencode.ai/config.json",
"lsp": {
"typescript": { "disabled": true },
"eslint": {
"command": ["npx", "vscode-eslint-language-server", "--stdio"],
"extensions": [".js", ".jsx", ".ts", ".tsx"]
},
"custom-lang": {
"command": ["my-langserver", "--stdio"],
"extensions": [".foo"]
}
}
}- Disable a server that is slow, noisy or wrong for the project - a bad diagnostic channel is worse than none.
- Add a server for a language OpenCode does not know, or to bring in a linter as an extra diagnostic source.
- Keep the command to something already in the project's toolchain so every developer gets the same diagnostics.
💡
Diagnostics are a filter, not a verdict. They catch type and import errors cheaply, which is most of what an agent gets wrong mechanically. They say nothing about whether the behaviour is correct - the test suite is still the gate that decides that.
When the server misleads
| Symptom | Cause | Fix |
|---|---|---|
| Errors in code that compiles fine | Server reading a stale or wrong tsconfig | Point the server at the right project file |
| Thousand of errors on open | Monorepo: server started at the wrong root | Configure per-package roots or disable the root server |
| Agent loops on a phantom error | Generated files in the include path | Exclude build output and generated directories |
| Diagnostics never appear | No server installed on this machine | Install it, then restart the session |
| Slow responses on every edit | Heavy server on a very large tree | Disable it and rely on the type-check command |
# reproduce what the agent sees, outside the agent
npx tsc --noEmit
npx tsc --noEmit -p packages/core/tsconfig.json
# if the compile is clean but the agent insists otherwise, the server
# is reading a different project - that is a config problem, not a code oneThe rule is simple: if tsc --noEmit is clean and the agent keeps reporting errors, the diagnostic channel is wrong. Fix the configuration or disable that server; do not let the agent argue with a broken oracle.
FAQ
Does OpenCode require language servers?
No. Everything works without them, but the agent then has to infer types and imports from reading source, which is slower and more error-prone. When a supported language server is installed, the quality of mechanical edits improves noticeably.
Why did enabling a linter as a language server help?
It turns lint rules into diagnostics the agent can read and act on immediately, in the same turn as the edit. That is faster and cheaper than discovering the same problem in a later CI run.
Related
Plan mode versus build mode MCP servers and custom tools
Last refreshed 2026-09-18.