Skip to content

Language Server Protocol (LSP)

Pythinker Code can connect to Language Server Protocol servers for semantic code intelligence: go-to-definition, find-references, hover, symbols, and call hierarchy.

Plugin-only servers

LSP servers are not configured in user or project TOML. They come only from installed plugins — inline lspServers in plugin.json or a plugin-root .lsp.json file. Pythinker does not bundle language-server binaries.

Enable executable plugin artifacts (plugins.external_exec = true or pythinker plugin enable <name>) so plugin-provided LSP subprocesses are allowed.

Server config schema

Each entry in lspServers (or the root object of .lsp.json) maps a server name to a config object:

json
{
  "my-server": {
    "command": "pylsp",
    "args": ["--check-parent-process"],
    "extensionToLanguage": { ".py": "python" },
    "env": { "VIRTUAL_ENV": "${VIRTUAL_ENV:-}" },
    "initializationOptions": {},
    "startupTimeout": 30.0,
    "maxRestarts": 3
  }
}
FieldRequiredDescription
commandyesExecutable to launch
argsnoAdditional CLI arguments
extensionToLanguageyesMaps file extensions to LSP language IDs
envnoExtra environment variables for the server process
initializationOptionsnoPassed verbatim in the LSP initialize request
startupTimeoutnoSeconds to wait for server ready (default 30.0, must be > 0)
maxRestartsnoMax automatic restarts on crash (default 3, 0 disables)

Values in command, args, and env support ${VAR} and ${VAR:-default} expansion against the process environment. Two plugin-local path variables are always available: ${PYTHINKER_PLUGIN_ROOT} (the plugin directory) and ${PYTHINKER_PLUGIN_DATA} (a writable per-plugin data directory). The CLAUDE_PLUGIN_ROOT / CLAUDE_PLUGIN_DATA spellings are accepted as aliases.

Agent tool

The LSP tool is available on the default agent and the coder subagent (not on read-only profiles such as code_reviewer). It exposes nine operations with 1-based line/character positions (editor-style).

OperationLSP method
goToDefinitiontextDocument/definition
findReferencestextDocument/references
hovertextDocument/hover
documentSymboltextDocument/documentSymbol
workspaceSymbolworkspace/symbol
goToImplementationtextDocument/implementation
prepareCallHierarchytextDocument/prepareCallHierarchy
incomingCallscallHierarchy/incomingCalls
outgoingCallscallHierarchy/outgoingCalls

Results from findReferences, goToDefinition, goToImplementation, and workspaceSymbol automatically filter out paths that match the project's .gitignore.

Servers start lazily on first use per language and stay alive for the session. Subagents share the root session's LSP processes.

Passive diagnostics

After WriteFile or StrReplaceFile, the session notifies open language servers and surfaces new compiler/linter diagnostics on the next turn via dynamic context injection (budget-capped). Diagnostics are labeled as LSP-reported, not agent-asserted.

Configuration

Feature switches only — in ~/.pythinker/config.toml:

toml
[lsp]
enabled = true
recommendation_disabled = false
recommendation_never = []

When you edit a file whose extension matches a discoverable but not-yet-installed plugin server, Pythinker may suggest installing that plugin (respecting recommendation_never and auto-disabling after repeated ignores).

Trust boundary

LSP servers run as subprocesses with the agent's privileges. Hover, symbol, and diagnostic text is treated as untrusted project content (same class as ReadFile output).