lsp-core -- Harness-Neutral LSP Engine (Core)
August 27, 2026 · View on GitHub
Generated: 2026-08-24 / f3642fcda
OVERVIEW
Harness-neutral LSP engine (@oh-my-opencode/lsp-core). Manages language server lifecycle, JSON-RPC transport, configuration merging, and tool definitions. Consumed by the MCP-layer packages lsp-tools-mcp and lsp-daemon. See parent packages/AGENTS.md.
KEY FILES
| File | Role |
|---|---|
src/lsp/manager.ts | LspManager: ref-counted client pool, init timeout, idle reaper, abort signals |
src/lsp/client.ts | LspClient: openFile, definition, references, symbols, diagnostics, rename |
src/lsp/client-wrapper.ts | withLspClient(): workspace root discovery, retry on dead connection, release |
src/lsp/connection.ts | LspClientConnection: initialize request with capabilities, settle delay |
src/lsp/json-rpc-connection.ts | Raw JSON-RPC 2.0 framing over stdio |
src/lsp/config-loader.ts | Load .codex/lsp-client.json (project + user), merge with builtins |
src/lsp/server-definitions.ts | BUILTIN_SERVERS (51 languages), LSP_INSTALL_HINTS, AUTO_INSTALLABLE_SERVERS |
src/lsp/server-resolution.ts | findServerForExtension(): map extension to installed server; substitutes the resolved absolute binary path into command[0] |
src/lsp/server-installation.ts | resolveServerBinary(): marker-gated repo-local lookup then PATH, with Windows extension handling; isServerInstalled() retained for existing consumers |
src/lsp/directory-diagnostics.ts | aggregateDiagnosticsForDirectory(): walk directory, cap files + diagnostics; AbortSignal cancels acquisition and per-file scans |
src/lsp/formatters.ts | Format locations, symbols, diagnostics, rename results, workspace edits |
src/lsp/workspace-edit.ts + workspace-edit-*.ts | applyWorkspaceEdit() / applyWorkspaceEditDetailed(): parse → fingerprint/snapshot → simulate → commit pipeline |
src/lsp/workspace-mutation-controller.ts | Lease/concurrency validation for workspace filesystem mutations |
src/lsp/fixtures/ | Test-only LSP servers/probes (workspace-edit server, diagnostics-freshness contract probe) |
src/post-edit/orchestration.ts | Post-edit diagnostics blocks (capped concurrency, not-configured cache) via ./post-edit subpath |
src/missing-dependency-result.ts | Shared missing-dependency MCP result shape via ./missing-dependency-result subpath |
src/tools/definitions.ts | LSP_MCP_TOOLS: 8 tool schemas exported to MCP |
src/tools/runtime.ts | executeLspTool() + coerceToolArguments() dispatch |
src/request-context.ts | runWithRequestContext() / contextCwd() / contextEnv() via AsyncLocalStorage |
src/mcp.ts | handleLspMcpRequest() + runMcpStdioServer(): MCP entry over mcp-stdio-core |
NOTES
- Tool surface: 8 tools:
lsp_diagnostics,lsp_goto_definition,lsp_find_references,lsp_symbols,lsp_prepare_rename,lsp_rename,lsp_status, andlsp_install_decision. Pinned bysrc/tool-surface.test.ts. - Subpath exports:
.,./tools,./request-context,./missing-dependency-result,./mcp,./post-edit, and wildcard./lsp/*— all point at source.ts, no dist build. - RequestContext seam:
request-context.tsusesAsyncLocalStorageso the MCP proxy can threadcwdandenvthrough shared daemon sessions. - Config priority: project
.codex/lsp-client.jsonbeats user~/.codex/lsp-client.jsonbeatsBUILTIN_SERVERS. - Binary resolution:
resolveServerBinary()probes a path-shaped command, then marker-gated repo-local bin directories walking up from the request cwd, thenPATH. A local bin directory is only trusted when a sibling project marker (package.json,pyproject.toml,Gemfile,go.mod, ...) proves it belongs to that ecosystem, so an unmarkednode_modules/.binis never trusted; the walk stops at a.gitboundary. Results are cached per process, keyed by working directory and command. Live QA driver:scripts/qa/local-binary-resolution-e2e.mjs. - Reaper:
LspManagerreaps clients idle longer thanIDLE_TIMEOUT_MS(default 5 min) or stuck initializing pastINIT_TIMEOUT_MS(default 30 s). - Workspace-edit safety: paths are canonicalized, prevalidated, simulated, and lease-guarded before commit; client wrappers reject paths outside the request context (
outside-context-workspace.ts). - Directory diagnostics cancellation:
aggregateDiagnosticsForDirectory()accepts anAbortSignal; it throws if aborted before acquisition, threads the signal intomanager.getClient()so a cold acquisition is cancellable, and checks it between files. An aborted cold start rejects withAbortErrorand leavesclientCount()at 0.