@spences10/pi-lsp
Language Server Protocol tools for Pi agents to inspect diagnostics, hovers, definitions, references, and symbols
Package details
Install @spences10/pi-lsp from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:@spences10/pi-lsp- Package
@spences10/pi-lsp- Version
0.1.0- Published
- Nov 6, 2026
- Downloads
- 1,773/mo · 1,189/wk
- Author
- spences10
- License
- MIT
- Types
- extension
- Size
- 178.1 KB
- Dependencies
- 1 dependency · 2 peers
Pi manifest JSON
{
"image": "https://raw.githubusercontent.com/spences10/my-pi/main/assets/pi-package-preview.png",
"extensions": [
"./dist/index.js"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
@spences10/pi-lsp

Prompt guidance uses Pi's named system-prompt sections. It coexists
with user SYSTEM.md and APPEND_SYSTEM.md files and other
extensions without replacing the complete prompt.
Give agents precise code intelligence instead of guesswork. pi-lsp
exposes language-server diagnostics, hovers, definitions, references,
and symbols as Pi tools so models can validate edits and navigate
typed codebases accurately.
When directly activated, the hover, definition, and document-symbol tools prefer Pi's strict JSON Schema sampling with closed, fully required schemas. LSP tools with optional arguments use normal tool calling for provider portability.
Installation
pi install npm:@spences10/pi-lsp
Local development from this monorepo:
pnpm --filter @spences10/pi-lsp run build
pi install ./packages/pi-lsp
# or for one run only
pi -e ./packages/pi-lsp
Required language servers
This package talks to language-server binaries installed globally on
PATH or locally in a project. Global installation makes one server
available across projects:
npm install -g typescript svelte-language-server
# or
pnpm add -g typescript svelte-language-server
Project-local development dependencies let a repository pin and share specific server versions:
npm install -D typescript svelte-language-server
# or
pnpm add -D typescript svelte-language-server
For TypeScript 6 and earlier, add typescript-language-server to the
same global or project-local command. Volta users can install global
tools with volta install typescript svelte-language-server.
Supported server discovery includes:
- TypeScript 7 / JavaScript via the project-local native
tsc --lsp --stdioserver - TypeScript 6 and earlier via
typescript-language-server --stdio - Svelte via
svelteserver - Python via
python-lsp-server, Basedpyright, or Pyright - Go via
gopls - Rust via
rust-analyzer - Ruby via
solargraph - Java via
jdtls - Lua via
lua-language-server
The TypeScript backend is selected by capability. A project-local
TypeScript installation takes priority. TypeScript 7 without
lib/tsserver.js uses its native tsc LSP, while classic project
installations use typescript-language-server. When a project does
not pin TypeScript, a TypeScript 7 tsc on PATH provides the native
LSP. /lsp status reports the selected backend and full command. A
TypeScript 7 native server that cannot start reports a specific setup
hint.
The classic typescript-language-server backend starts with
initializationOptions.tsserver.useSyntaxServer: "never". This keeps
navigation requests on the full semantic server instead of accepting
partial answers from the lightweight syntax server while the project
loads. The first request can take longer. This setting is not sent to
TypeScript 7's native server or other language servers.
Python server selection
Python keeps pylsp when it is available, so installing another type
checker does not replace an existing pylsp setup or its plugins. When
pylsp is missing, project-local Basedpyright or Pyright takes priority
over global tools. The nearest project installation wins; within the
same directory, Basedpyright takes priority over Pyright. Global
fallback uses Basedpyright before Pyright.
Set MY_PI_LSP_PYTHON_SERVER to pylsp, basedpyright, or pyright
to select a specific backend. Unset it or use auto for the default
selection above. For example:
MY_PI_LSP_PYTHON_SERVER=pyright pi
An explicit selection never switches to another backend. Missing or
failed servers report an error and an installation hint. Install with
pip install python-lsp-server, pip install basedpyright, or
pip install pyright. Pyright-family servers use --stdio.
Python discovery checks executable files in ancestor .venv/bin
folders (.venv/Scripts on Windows), node_modules/.bin, and PATH.
Directories, broken links, and non-executable files are skipped. On
Windows, use native executables such as the .exe launchers from pip;
.cmd and .bat wrappers are not supported by the shell-free client.
/lsp status shows the selected backend and resolved command.
Server discovery does not select the Python interpreter used for analysis. Configure the server's interpreter or virtual environment settings separately when needed.
Project-local language servers
Project-local binaries in node_modules/.bin and Python .venv
folders are selected automatically by the discovery rules above, in
both interactive and headless sessions. There is no separate
per-binary approval prompt or LSP trust store.
MY_PI_LSP_PROJECT_BINARY is no longer used. /lsp status shows the
selected backend and resolved command for running and idle servers.
Language-server environment
LSP filters its language-server environment directly, passing only
PATH, PI_CODING_AGENT_DIR, home/user, locale (LANG and LC_*),
terminal/color, shell, temporary-directory, and CI variables by
default. Provider credentials, Pi session metadata, NODE_OPTIONS,
and other ambient variables are not inherited unless explicitly
allowlisted.
Use MY_PI_LSP_ENV_ALLOWLIST=NAME,OTHER_NAME to pass toolchain
configuration such as RUSTUP_HOME, CARGO_HOME, or JAVA_HOME when
needed. MY_PI_CHILD_ENV_ALLOWLIST remains supported; entries from
both lists are combined.
This reduces ambient credential exposure, but is not a sandbox: language servers retain the current user's filesystem permissions.
Tools
The extension registers LSP-backed Pi tools for:
- diagnostics
- hover
- definitions
- references
- document symbols
LSP tools use Pi's codemode exposure: they are callable from scripts
without being declared individually to the model. Enable codemode with
pi --tools +codemode or upstream Pi's defaultTools setting. No
global tool-exposure settings are changed by this extension.
text(await tools.lsp_diagnostics_many({ files: ['src/index.ts'] }));
Use searchTools('lsp') inside codemode to discover the tools. They
can also be activated directly with Pi's tool selection, for example
pi --tools +lsp_hover. Automatic diagnostics do not require codemode
or direct LSP tool activation.
A diagnostics result is clean only when the language server supplied
an empty list for the current document. If the server sends nothing
before wait_ms expires, the tool reports validation incomplete,
and lsp_diagnostics_many counts the file as incomplete and not as
clean. An incomplete result is not a pass: retry with a larger
wait_ms or use another check. Versioned diagnostics for a closed
document or an earlier document version are ignored. Versions are
never reused across reopenings, and a pending push wait or pull
request cannot validate a snapshot that changed while it was waiting.
Documents stay open between requests, with later checks rereading the file and sending changes when its contents differ. This avoids versionless empty cleanup diagnostics from a close racing with the next check. Versionless push results still rely on the server's ordering. Open documents and their cached text are released when the server stops, restarts, or the Pi session exits.
Automatic diagnostics
Successful edit and write calls queue supported files for one
batch at turn end, including calls made inside codemode. Repeated
edits to a file are checked once against its final contents. The
extension reuses the diagnostics tool's collection and formatting,
checking up to eight files concurrently.
Edits themselves do not wait for LSP. The turn boundary waits for server startup and up to 1500 ms of diagnostics per file before the next model response. A session message records the results, including clean files, errors, warnings, and incomplete checks. The final edit is checked even when it was the last tool call. Cancelled turns and session changes discard pending feedback.
Bash commands, external editors, failed edits, and unsupported files are not tracked. Use explicit diagnostics after Bash or external changes, or when automatic feedback is incomplete. Silence is not proof that a file is clean.
Model reminder
When LSP tools, codemode, or tool search are active, the extension
reminds the model to use LSP for code questions and reuse current
automatic feedback rather than repeating checks. Explicit diagnostics
remain available for missing or incomplete feedback, preferring
lsp_diagnostics_many for batches. Passed diagnostics can be reused
for review, commit, and push if their inputs are unchanged. Only
affected or previously missing diagnostics need to run; a commit or
push request alone does not require another run.
Commands
/lsp status
/lsp list
/lsp restart all
/lsp restart <language>
Use /lsp status to inspect active clients and /lsp restart after
dependency installs or language-server crashes.
Language servers stop after five minutes without an active LSP request
and start again on demand. Set MY_PI_LSP_IDLE_TIMEOUT_MS to a
positive timeout in milliseconds, or set it to 0 to keep idle
servers running until the Pi session exits.
Using from a custom harness
import lsp from '@spences10/pi-lsp';
// pass `lsp` as an ExtensionFactory to your Pi runtime
For harnesses that need to provide their own language-server client factory, use the named extension factory:
import { create_lsp_extension } from '@spences10/pi-lsp';
const lsp = create_lsp_extension({ create_client });
The package also exports CreateLspExtensionOptions,
should_inject_lsp_prompt, and LspClientLike for custom harnesses
and tests that need to share the same prompt-gating or client seam.
my-pi includes this package.
Development
Package scripts build transitive workspace dependencies first, then
run local tools through Vite+ with vp exec.
pnpm --filter @spences10/pi-lsp run check
pnpm --filter @spences10/pi-lsp run test
pnpm --filter @spences10/pi-lsp run build
License
MIT