Description
LSP diagnostics context injection silently fails on every write tool call. The fetch_diagnostics function in zeph-core/src/lsp_hooks/diagnostics.rs expects mcpls get_diagnostics to return a bare JSON array ([{...}, ...]), but mcpls v0.3.6 returns a JSON object {"diagnostics": [{...}, ...]} (serialized DiagnosticsResult struct).
The result is that every diagnostics fetch silently returns None and no LSP context is ever injected into the prompt, even when lsp.diagnostics.enabled = true and mcpls is connected and available.
Reproduction Steps
- Configure
[lsp] with enabled = true, mcp_server_id = "mcpls", and [lsp.diagnostics] enabled = true
- Add
[[mcp.servers]] with id = "mcpls" command pointing to the mcpls binary
- Run the agent in full mode, ask it to write a Rust file
- Enable
RUST_LOG=debug and observe the log
Expected Behavior
After write tool completes:
- Background diagnostics task spawns (already works:
"LSP hook: spawning diagnostics fetch")
get_diagnostics call completes and JSON is parsed as [{...}]
- If diagnostics exist,
[lsp diagnostics] System message is injected before the next LLM call
Actual Behavior
Debug log shows:
LSP diagnostics: failed to parse response JSON path="..." error=invalid type: map, expected a sequence at line 1 column 0
The parse fails silently, None is returned, no LSP context is injected.
Root Cause
diagnostics.rs:103:
let diagnostics: Vec<serde_json::Value> = match serde_json::from_str(json_text) {
mcpls get_diagnostics serializes DiagnosticsResult { diagnostics: Vec<Diagnostic> }, which produces:
The parser expects a bare array [{...}].
This mismatch has existed since the initial LSP injection implementation — the expected format was never correct.
Fix
In crates/zeph-core/src/lsp_hooks/diagnostics.rs, replace the direct Vec parse with either:
- Parse as
serde_json::Value, then extract .get("diagnostics") array (preferred — more resilient)
- Define a wrapper struct
struct DiagnosticsResponse { diagnostics: Vec<serde_json::Value> } and parse into that
Environment
- Version: 0.20.1 (HEAD 4e77b4f)
- mcpls version: 0.3.6
- Features: full (with
[lsp] config)
- Detected in: CI-661 live testing, 2026-05-05
Logs / Evidence
DEBUG zeph_core::lsp_hooks: LSP after_tool: checking availability tool="write"
DEBUG zeph_core::lsp_hooks: lsp_hooks: is_available check complete available=true
DEBUG zeph_core::lsp_hooks: LSP hook: spawning diagnostics fetch tool="write" path=.local/testing/data/lsp_test.rs
DEBUG zeph_core::lsp_hooks::diagnostics: LSP diagnostics: calling get_diagnostics path=".local/testing/data/lsp_test.rs" timeout_secs=10
DEBUG zeph_core::lsp_hooks::diagnostics: LSP diagnostics: failed to parse response JSON path=".local/testing/data/lsp_test.rs" error=invalid type: map, expected a sequence at line 1 column 0
Description
LSP diagnostics context injection silently fails on every
writetool call. Thefetch_diagnosticsfunction inzeph-core/src/lsp_hooks/diagnostics.rsexpects mcplsget_diagnosticsto return a bare JSON array ([{...}, ...]), but mcpls v0.3.6 returns a JSON object{"diagnostics": [{...}, ...]}(serializedDiagnosticsResultstruct).The result is that every diagnostics fetch silently returns
Noneand no LSP context is ever injected into the prompt, even whenlsp.diagnostics.enabled = trueand mcpls is connected and available.Reproduction Steps
[lsp]withenabled = true,mcp_server_id = "mcpls", and[lsp.diagnostics] enabled = true[[mcp.servers]]withid = "mcpls"command pointing to themcplsbinaryRUST_LOG=debugand observe the logExpected Behavior
After
writetool completes:"LSP hook: spawning diagnostics fetch")get_diagnosticscall completes and JSON is parsed as[{...}][lsp diagnostics]System message is injected before the next LLM callActual Behavior
Debug log shows:
The parse fails silently,
Noneis returned, no LSP context is injected.Root Cause
diagnostics.rs:103:mcpls
get_diagnosticsserializesDiagnosticsResult { diagnostics: Vec<Diagnostic> }, which produces:{"diagnostics": [{...}]}The parser expects a bare array
[{...}].This mismatch has existed since the initial LSP injection implementation — the expected format was never correct.
Fix
In
crates/zeph-core/src/lsp_hooks/diagnostics.rs, replace the directVecparse with either:serde_json::Value, then extract.get("diagnostics")array (preferred — more resilient)struct DiagnosticsResponse { diagnostics: Vec<serde_json::Value> }and parse into thatEnvironment
[lsp]config)Logs / Evidence