Skip to content

feat(lsp): add lsp AI tool for code navigation (TS/JS)#293

Merged
XingYu-Zhong merged 2 commits into
KunAgent:developfrom
whitelonng:feat/lsp-tool
Jun 14, 2026
Merged

feat(lsp): add lsp AI tool for code navigation (TS/JS)#293
XingYu-Zhong merged 2 commits into
KunAgent:developfrom
whitelonng:feat/lsp-tool

Conversation

@whitelonng

Copy link
Copy Markdown
Contributor

Summary

Adds an lsp AI tool that lets the agent query a language server for code navigation. Currently supports TypeScript / JavaScript via typescript-language-server. This is the AI-tool-only version (no editor integration); a follow-up can add it to the Monaco/CodeMirror editor once that lands.

Supported operations

Operation Input Returns
goToDefinition filePath, line, character Location[]
findReferences filePath, line, character Location[]
hover filePath, line, character Hover (type info / docs)
documentSymbol filePath Symbols in file
workspaceSymbol query Symbols across project
goToImplementation filePath, line, character Location[]

Positions are 1-based (as editors display them); the tool converts to 0-based internally and back to 1-based in output.

Architecture

Agent calls `lsp` tool
  → builtin-lsp-tool.ts (LocalTool definition)
    → lsp-client.ts (JSON-RPC 2.0 over stdio)
      → spawn('typescript-language-server', ['--stdio'])

No external LSP client library (vscode-languageserver-protocol / vscode-jsonrpc). The client implements minimal Content-Length framing and request/response matching in ~200 lines — this avoids adding a new dependency to kun/ and keeps the PR self-contained.

Files

File Lines Description
kun/src/adapters/tool/lsp-client.ts ~330 Session management (reference-counted per workspace, 30s idle cleanup), JSON-RPC framing, initialize handshake, didOpen/didClose, 6 query methods
kun/src/adapters/tool/builtin-lsp-tool.ts ~270 LocalTool definition: hand-written JSON Schema (Kun convention), position conversion, result simplification (file:// → path, symbol kind numbers → names)
kun/src/adapters/tool/builtin-tools.ts +2 Register in buildBuiltinLocalTools()

Key design decisions

  1. No new kun/ dependency. JSON-RPC framing is hand-rolled (~100 lines) rather than pulling in vscode-languageserver-protocol. The framing logic is straightforward (Content-Length header + body) and has no edge cases for the query-only operations we support.
  2. Reference-counted sessions. Like the bash tool's session map, LSP servers are keyed by workspace root and kept alive between tool calls. The last release schedules a 30s cleanup timer; a new acquire cancels it. This avoids respawning the server on every call.
  3. Graceful degradation. If typescript-language-server isn't installed, the tool returns a helpful error (Install with: npm install -g typescript-language-server typescript) instead of crashing the agent loop.
  4. Read-only. All operations are queries — policy: 'auto', toolKind: 'tool_call'. No approval needed.
  5. Result simplification. Raw LSP responses contain file:// URIs, 0-based positions, and numeric symbol kinds. The tool converts these to plain paths, 1-based positions, and human-readable kind names so the output is compact for the agent's context window.

Prerequisites

The user (or their project) must have typescript-language-server installed:

npm install -g typescript-language-server typescript

Or locally: npm install -D typescript-language-server typescript (the tool checks node_modules/.bin/ first).

Verification

  • kun/ typecheck passes (tsc --noEmit)
  • Root project typecheck passes
  • kun/ tool tests pass (24/24)
  • No new test failures introduced (3 pre-existing failures in compaction tests are unrelated to this PR — verified they fail on clean develop too)

Future work (not in this PR)

  • Support more languages (pyright, gopls, rust-analyzer) — the client is language-agnostic; just needs a config table + binary detection
  • Editor integration (completion, go-to-definition in Monaco/CodeMirror)
  • Automatic server installation prompt when missing

Adds an 'lsp' tool that lets the AI agent query a language server for
code navigation: goToDefinition, findReferences, hover, documentSymbol,
workspaceSymbol, goToImplementation. Currently supports TypeScript /
JavaScript via typescript-language-server.

The tool spawns typescript-language-server as a child process and speaks
JSON-RPC 2.0 over stdio. No external LSP client library is needed — the
client implements minimal Content-Length framing and request/response
matching (~200 lines).

Design:
- lsp-client.ts: session management (reference-counted per workspace,
  30s idle cleanup), JSON-RPC framing, initialize handshake, didOpen/
  didClose document sync, and the 6 query methods
- builtin-lsp-tool.ts: the LocalTool definition with zod-free hand-written
  JSON Schema (matching Kun's existing tool convention), position
  conversion (1-based input → 0-based LSP), and result simplification
  (strips file:// URIs to paths, converts positions back to 1-based,
  maps symbol kind numbers to names)

The server binary must be installed (npm install -g typescript-language-
server typescript). If missing, the tool returns a helpful error with
install instructions instead of crashing.

Registered as a builtin tool alongside read/bash/grep/etc. via
buildBuiltinLocalTools(). Read-only operations, policy 'auto'.
Three fixes from code review:

1. Process leak: export shutdownAllLspSessions() and register a
   process.on('exit') handler that SIGKILLs all active LSP servers
   synchronously. Previously, if the host process exited within the
   30s idle window, the language server (and its tsserver child) would
   become orphaned.

2. Acquire race: sessions map now stores Promise<LspSession> instead of
   LspSession. The first caller writes its Promise before any await;
   concurrent callers await the same Promise. Previously two concurrent
   tool calls could each spawn their own server.

3. Server crash: the 'exit' and 'error' handlers now call killSession()
   which rejects all pending requests immediately. Previously a server
   crash would leave pending requests hanging until their 30s timeout.
@XingYu-Zhong

Copy link
Copy Markdown
Collaborator

hope Future work can fast coming,lsp is very useful

@XingYu-Zhong XingYu-Zhong merged commit a3ff1f3 into KunAgent:develop Jun 14, 2026
@whitelonng

whitelonng commented Jun 14, 2026 via email

Copy link
Copy Markdown
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants