Design exercise: LSP support via existing extension points (à la Claude Code's lspServers plugins)
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 152
- Forks
- 16
- Avg merge
- 14h 48m
- Merged PRs (30d)
- 536
Description
Claude Code ships native LSP support and it's worth studying as a design exercise: could mecatl get the same capability purely through its existing extension points — no changes to the core harness kit?
What Claude Code does (read from the shipped binary, 2.1.224)
-
The harness embeds a real LSP client. It speaks the protocol directly to a language server:
textDocument/didOpen|didChange|didSave,publishDiagnostics,definition,references,implementation,hover,documentSymbol, call hierarchy, semantic tokens. -
Servers are declared, not implemented, by plugins. A plugin manifest carries an
lspServersfield — inline config or a path to a.lsp.json. The officialgopls-lspplugin is nothing but this (source):"lspServers": { "gopls": { "command": "gopls", "extensionToLanguage": { ".go": "go" } } }The plugin bundles no binary —
goplsmust be on PATH. The harness spawns it lazily when a matching file is touched. -
The payoff is zero prompt-context cost: no tool schemas injected. Diagnostics come back attached to edit results; semantic navigation is available without the model asking. Contrast with
gopls mcp(the headless MCP server gopls also ships), which exposes explicit tools (go_symbol_references,go_package_api, …) at a per-session context tax.
The design exercise
How close can mecatl get without touching the core?
- Diagnostics after edits looks like a
PostToolUsehook on Edit/Write: rungopls check <file>(or a persistent server behind a small shim) and emit findings back into the transcript. Does our hook output channel carry enough to make this useful, or does it only allow/block? - Navigation/references as tools looks like MCP territory (
gopls mcpexists today) — but that reintroduces the context tax the native design avoids. Is there a middle path, e.g. ToolSearch-style progressive disclosure over an LSP-backed toolset? - Lifecycle is the hard part: a long-lived stateful subprocess per session, lazy spawn on first matching file,
didOpen/didChangesynchronization with the file tools' writes, workspace-root scoping (ouros.Rootcontainment), shutdown on session end. Which of our seams (CommandRunner? hooks? something new) can own a process with that lifecycle? - If the answer is "none, cleanly" — that's the finding: LSP may be a genuine core capability (a new port, like the provider port), and the exercise tells us where the extension-point boundary actually sits.
Worth noting Claude Code treats it as core too (--bare disables it alongside hooks and plugin sync), while keeping per-language config in plugin space. That split — protocol client in core, server registry in extensions — may be the interesting part to emulate.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by tracing the existing PostToolUse hooks around Edit/Write and the CommandRunner, then inspect how os.Root containment and session shutdown are handled. Evaluate whether these extension points can support LSP diagnostics, navigation, and a stateful subprocess lifecycle without core changes. Done means documenting a viable design or clearly identifying where a new core capability is required.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- devtools
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 30/100