LSP 3.17 Conformance¶
agent-lsp was built directly against the LSP 3.17 specification. Each protocol area was implemented by reading the relevant spec section and verified through integration testing against real language servers (gopls, rust-analyzer, typescript-language-server, pyright, jdtls, clangd, intelephense).
The spec section links below are anchored directly into the specification.
Method Coverage Matrix¶
Every LSP 3.17 method and its MCP surface. "Protocol only" means the method is correctly handled at the transport layer (capabilities declared, responses sent) but not exposed as an MCP tool.
Text Document Methods¶
| LSP Method | Spec | MCP Tool | Status |
|---|---|---|---|
textDocument/didOpen |
§3.15.7 | open_document |
✓ |
textDocument/didClose |
§3.15.9 | close_document |
✓ |
textDocument/didChange |
§3.15.8 | — | ✓ protocol only (sent internally by open_document for already-open files and by simulation edits) |
textDocument/publishDiagnostics |
§3.17.1 | get_diagnostics |
✓ |
textDocument/hover |
§3.15.11 | get_info_on_location |
✓ |
textDocument/completion |
§3.15.13 | get_completions |
✓ |
textDocument/signatureHelp |
§3.15.14 | get_signature_help |
✓ |
textDocument/definition |
§3.15.2 | go_to_definition |
✓ |
textDocument/references |
§3.15.8 | get_references |
✓ |
textDocument/documentSymbol |
§3.15.20 | get_document_symbols |
✓ |
textDocument/codeAction |
§3.15.22 | get_code_actions |
✓ |
textDocument/formatting |
§3.15.16 | format_document |
✓ |
textDocument/rename |
§3.15.19 | rename_symbol |
✓ |
textDocument/typeDefinition |
§3.15.3 | go_to_type_definition |
✓ |
textDocument/implementation |
§3.15.4 | go_to_implementation |
✓ |
textDocument/declaration |
§3.15.5 | go_to_declaration |
✓ |
textDocument/prepareRename |
§3.15.19 | prepare_rename |
✓ |
textDocument/selectionRange |
§3.15.29 | — | ✗ not yet implemented |
textDocument/foldingRange |
§3.15.28 | — | ✗ not yet implemented |
textDocument/documentHighlight |
§3.15.10 | get_document_highlights |
✓ |
textDocument/rangeFormatting |
§3.15.17 | format_range |
✓ |
textDocument/codeLens |
§3.15.21 | — | ✗ not yet implemented |
textDocument/inlayHint |
§3.17.11 | get_inlay_hints |
✓ |
textDocument/semanticTokens |
§3.16.12 | get_semantic_tokens |
✓ |
textDocument/prepareCallHierarchy |
§3.16.5 | call_hierarchy |
✓ |
callHierarchy/incomingCalls |
§3.16.5 | call_hierarchy |
✓ |
callHierarchy/outgoingCalls |
§3.16.5 | call_hierarchy |
✓ |
textDocument/prepareTypeHierarchy |
§3.17.12 | type_hierarchy |
✓ |
typeHierarchy/supertypes |
§3.17.12 | type_hierarchy |
✓ |
typeHierarchy/subtypes |
§3.17.12 | type_hierarchy |
✓ |
Workspace Methods¶
| LSP Method | Spec | MCP Tool | Status |
|---|---|---|---|
workspace/symbol |
§3.15.21 | get_workspace_symbols |
✓ |
workspace/configuration |
§3.16.14 | — | ✓ protocol only (server-initiated) |
workspace/executeCommand |
§3.16.13 | execute_command |
✓ |
workspace/didChangeWatchedFiles |
§3.16.8 | did_change_watched_files (+ auto-watch) |
✓ |
workspace/didChangeWorkspaceFolders |
§3.16.5 | add_workspace_folder, remove_workspace_folder |
✓ |
Protocol Infrastructure¶
| Area | Status |
|---|---|
Lifecycle (initialize → initialized → shutdown) |
✓ |
Progress protocol ($/progress begin/report/end) |
✓ |
window/workDoneProgress/create (server-initiated) |
✓ |
client/registerCapability (server-initiated) |
✓ |
| Unrecognized server requests | ✓ (null response) |
| Message framing (Content-Length, UTF-8 byte count) | ✓ |
| JSON-RPC 2.0 shapes | ✓ |
| LSP error codes (-32601, -32002) | ✓ |
| Process crash → pending promise rejection | ✓ |
Lifecycle (§3.15.1–3.15.4)¶
- Correct
initialize→initialized→shutdownsequence - Graceful async shutdown via
SIGINT/SIGTERM. The LSP subprocess is never orphaned on exit - Client capabilities declared for every feature used:
hover,completion,references,definition,implementation,typeDefinition,declaration,codeAction,publishDiagnostics,window.workDoneProgress,workspace.configuration,workspace.didChangeWatchedFiles - Server capabilities checked before sending requests. If a server doesn't declare
hoverProvider,completionProvider,referencesProvider, orcodeActionProvider, the request is skipped rather than being sent and silently returning empty results initializetimeout set to 300s to accommodate JVM-based servers (jdtls) that require 60-90s for cold OSGi container startup- LSP process crash immediately rejects all pending promises, so callers fail fast rather than waiting for individual timeouts
Progress Protocol (§3.18)¶
window/workDoneProgress/create: the progress token is pre-registered inactiveProgressTokensbefore the response is sent, so subsequent$/progressnotifications are always recognized$/progressbegin/report/end: all threeWorkDoneProgresskinds are handled:begin: token added to active setreport: logged at debug levelend: token removed; when active set reaches zero, workspace-ready resolvers are notifiedwaitForWorkspaceReady()blockstextDocument/referencesrequests until all active progress tokens complete, ensuring gopls has finished workspace indexing before reference queries are sent
Server-Initiated Requests¶
All three server-initiated request types sent by gopls (and common in other LSP servers) are handled:
workspace/configuration (§3.16.14)¶
Responds with an array of null values matching params.items.length. Without this response, gopls blocks workspace loading and $/progress end never fires, so waitForWorkspaceReady() would hang indefinitely.
window/workDoneProgress/create¶
Responds with null (the required result). The progress token is extracted from params.token and pre-registered before responding, ensuring the subsequent $/progress begin notification is recognized.
client/registerCapability¶
Responds with null. Dynamic capability registration is acknowledged without modifying any state.
All unrecognized server-initiated requests also receive a null response to unblock the server rather than timing out.
Message Framing (§3.4)¶
- Content-Length header uses the UTF-8 byte length of the content (not the character count)
- Delimiter is
\r\n\r\nas required - Buffer overflow (>10MB) discards the entire buffer rather than keeping tail bytes, which would guarantee starting mid-message
JSON-RPC 2.0 (§3.3)¶
- Request shape:
{ jsonrpc: "2.0", id, method, params? }(correct) - Response shape:
{ jsonrpc: "2.0", id, result? | error? }(correct) - Notification shape:
{ jsonrpc: "2.0", method, params? }(noid) (correct) - IDs are monotonically incrementing integers
Error Codes (§3.6)¶
LSP-defined error codes are handled distinctly:
| Code | Name | Handling |
|---|---|---|
-32601 |
MethodNotFound | Logged as warning, indicates an unsupported feature |
-32002 |
ServerNotInitialized | Logged as warning, indicates a sequencing issue |
| All others | — | Logged at debug level |
Response Shape Normalization¶
textDocument/hover (§3.15.11)¶
The Hover.contents field can be one of three shapes. All are handled in priority order:
MarkupContent(current spec):{ kind: "markdown" | "plaintext", value: string }.kindis checked first to distinguish rendering intentMarkedString[](deprecated): array ofstring | { language, value }, joined with newlines- Plain string (deprecated MarkedString): returned as-is
textDocument/completion (§3.15.13)¶
Both response shapes are handled:
- CompletionItem[]: returned directly
- CompletionList ({ isIncomplete: boolean, items: CompletionItem[] }): items extracted
textDocument/codeAction (§3.15.22)¶
CodeActionContext.diagnostics is populated with diagnostics from documentDiagnostics whose range overlaps the requested range, enabling diagnostic-specific quick fixes. Sending an empty array would prevent servers from offering fixes tied to visible errors.
textDocument/publishDiagnostics (§3.17.1)¶
versionSupport: falsedeclared in client capabilities, so the server omits the optionalversionfieldurianddiagnosticsdestructured correctly;urivalidated as a string before processing- Diagnostics stored per-URI and used to populate
codeActioncontext andwaitForFileIndexedreadiness detection
Previously Non-Conformant (Fixed)¶
These issues were identified via spec audit and corrected:
| Issue | Spec Reference | Fix |
|---|---|---|
notifications/resources/update (wrong method name) |
MCP spec | Corrected to notifications/resources/updated |
UnsubscribeRequest.params.context (field doesn't exist in MCP schema) |
MCP spec | Subscription contexts now tracked server-side in a Map<uri, context> |
process.on('exit', async), await never completes |
§3.15.4 | Replaced with SIGINT/SIGTERM handlers |
workspace/configuration not responded to |
§3.16.14 | Added handler; this was blocking gopls workspace loading |
window/workDoneProgress/create response in wrong code path |
§3.18 | Moved to server-initiated request handler block |
rootPath sent in initialize params |
§3.15.1 | Removed. Deprecated in favour of rootUri; rootUri itself deprecated in favour of workspaceFolders (also sent) |
Empty diagnostics: [] in codeAction context |
§3.15.22 | Replaced with overlapping diagnostics filter |
MarkupContent.kind ignored in hover response |
§3.15.11 | kind now checked before accessing value |