Skip to content

mcpls does not recover after its LSP server subprocess dies #249

Description

@bug-ops

Description

When the LSP server process spawned by mcpls (e.g. rust-analyzer) dies mid-session — killed externally, OOM, or a segfault — mcpls surfaces a clean, well-formed error on the in-flight and all subsequent tool calls for that language, but never attempts to restart/respawn the server. The session remains permanently degraded for that language until the entire mcpls process is restarted by the client.

Grepped the codebase for restart/respawn/supervision logic: none exists. The only reference is a forward-looking comment at crates/mcpls-core/src/bridge/translator.rs:170-171 ("during background init, so this can't race a restart. If server restart/supervision is ever added, ...") confirming this was never implemented.

This matters for any long-running AI agent session — a single rust-analyzer crash (which does happen under memory pressure or on malformed workspace state) silently and permanently removes all LSP-backed tools for that language, with no recovery path other than the client noticing and restarting the whole mcpls process.

Reproduction Steps

  1. Start mcpls with a single-language config (e.g. rust-analyzer).
  2. Initialize and confirm a tool call works, e.g. get_hover returns real hover info.
  3. Kill only the rust-analyzer child process spawned by mcpls (not mcpls itself): pgrep -P <mcpls_pid> to find it, then kill -9 <pid>.
  4. Immediately call get_hover again — returns {"error":{"code":-32603,"message":"LSP server process terminated unexpectedly"}}.
  5. Wait 8+ seconds and call get_hover again — same error, indefinitely. No respawn attempt observed.

Expected Behavior

At minimum, one of:

  • mcpls detects the dead child process and respawns/re-initializes it on the next tool call for that language (transparent recovery), or
  • if auto-restart is an intentional non-goal, the error message should say so explicitly (e.g. "language server died and will not be restarted; restart mcpls to recover") rather than implying a possibly-transient per-call failure.

Actual Behavior

Silent, permanent, unrecoverable failure for that language's tools, with an error message ("terminated unexpectedly") that reads as if a retry might succeed, when no retry ever will.

Environment

  • Version: mcpls 0.3.7, commit 2c8ad7f (HEAD as of this cycle, no commits since)
  • Config: single rust-analyzer server, .local/testing/scratch/cfg_single_rust.toml
  • Features: default

Logs / Evidence

Live-tested via MCP stdio JSON-RPC harness (.local/testing/scratch/test_concurrent_and_crash.py, Scenario B):

pre-check: hover succeeds, returns Translator struct doc
[kill -9 on the rust-analyzer child PID only]
+3s:  {"error":{"code":-32603,"message":"LSP server process terminated unexpectedly"}}
+11s: {"error":{"code":-32603,"message":"LSP server process terminated unexpectedly"}}  (same, no recovery)

Note: the kill targeted only the specific rust-analyzer child of the test's own mcpls process (verified via pgrep -P <mcpls_pid>), not a blanket pkill -f rust-analyzer, to avoid disturbing unrelated rust-analyzer instances on the same machine.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium: suboptimal behavior, minor inconsistencybugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions