Description
handle_diagnostics in Hybrid mode acquires Arc<Mutex<Translator>> and holds it for the entire duration of a pull_diagnostics LSP request (30 s timeout). Every other MCP tool handler acquires the same lock. Under concurrent tool calls — e.g. get_hover + get_diagnostics + get_references — the non-diagnostics requests stall for up to 30 seconds waiting for the lock to release.
Additionally, the new ensure_open stat-on-access adds 2–3 syscalls (stat + possible read + re-stat) under the same lock per tool call, compounding under concurrent usage.
Reproduction Steps
- Configure mcpls with a Hybrid diagnostics mode and a slow or busy LSP server
- Send concurrent MCP requests:
get_hover and get_diagnostics simultaneously
- Observe:
get_hover stalls until pull_diagnostics times out (up to 30 s)
Expected Behavior
Non-diagnostics tool calls should not be blocked by an in-flight get_diagnostics request.
Actual Behavior
All MCP tool calls share a single Translator mutex. handle_diagnostics holds it across the full LSP round-trip.
Affected Code
crates/mcpls-core/src/mcp/server.rs:59 — all handlers acquire translator.lock().await
crates/mcpls-core/src/bridge/translator.rs — pull_diagnostics awaits 30 s under lock
Fix Direction
Extract per-language-server LSP I/O behind per-server locks (or per-request channels) so the global translator lock is only held for in-memory state mutations, not across LSP network round-trips.
Environment
- Affects all LSP servers in Hybrid diagnostics mode
- Severity increases with number of concurrent MCP clients / tools
Description
handle_diagnosticsinHybridmode acquiresArc<Mutex<Translator>>and holds it for the entire duration of apull_diagnosticsLSP request (30 s timeout). Every other MCP tool handler acquires the same lock. Under concurrent tool calls — e.g.get_hover+get_diagnostics+get_references— the non-diagnostics requests stall for up to 30 seconds waiting for the lock to release.Additionally, the new
ensure_openstat-on-access adds 2–3 syscalls (stat + possible read + re-stat) under the same lock per tool call, compounding under concurrent usage.Reproduction Steps
get_hoverandget_diagnosticssimultaneouslyget_hoverstalls untilpull_diagnosticstimes out (up to 30 s)Expected Behavior
Non-diagnostics tool calls should not be blocked by an in-flight
get_diagnosticsrequest.Actual Behavior
All MCP tool calls share a single
Translatormutex.handle_diagnosticsholds it across the full LSP round-trip.Affected Code
crates/mcpls-core/src/mcp/server.rs:59— all handlers acquiretranslator.lock().awaitcrates/mcpls-core/src/bridge/translator.rs—pull_diagnosticsawaits 30 s under lockFix Direction
Extract per-language-server LSP I/O behind per-server locks (or per-request channels) so the global translator lock is only held for in-memory state mutations, not across LSP network round-trips.
Environment