MoonshotAI / MoonshotAI/kimi-cli

MCP connection failure crashes Web UI worker instead of graceful degradation

Open
#1,766 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
11.4k
Forks
1.3k
Avg merge
9h 47m
Merged PRs (30d)
2

Description

Problem

When an MCP server fails to connect (e.g., port conflict), the Web UI session worker crashes entirely instead of continuing without MCP tools. This causes messages to get stuck in "thinking" state indefinitely, and the frontend becomes unresponsive.

Reproduction Steps

  1. Configure an MCP server that uses a fixed port (e.g., chrome-local-bridge on port 10086)
  2. Open a first kimi-cli TUI window — the MCP server starts successfully
  3. In the same session, execute /web to switch to Web UI
  4. Send a message in Web UI
  5. The session worker tries to start, attempts to connect the same MCP server, but the port is already in use by the first TUI window
  6. MCPRuntimeError is thrown, the worker process crashes
  7. Message stays stuck in "thinking" forever

Expected Behavior

MCP connection failure should be a graceful degradation:

  • Log a warning about the failed MCP server
  • Continue running without that MCP server's tools
  • The conversation should proceed normally

Actual Behavior

  • wait_for_background_mcp_loading() throws MCPRuntimeError
  • Exception propagates uncaught through _agent_loop()
  • Entire worker process exits
  • WebSocket _read_loop dies without emitting error/idle status
  • Frontend message remains stuck in "thinking"

Root Cause

In kimi_cli/soul/kimisoul.py, _agent_loop() (around line 680):

try:
    await self.wait_for_background_mcp_loading()
finally:
    if loading:
        wire_send(StatusUpdate(mcp_status=self._mcp_status_snapshot()))
        wire_send(MCPLoadingEnd())

The try/finally only ensures MCPLoadingEnd is sent, but does not catch MCPRuntimeError. The exception bubbles up and crashes the agent loop.

Additionally, in kimi_cli/web/runner/process.py, _read_loop() catches the unexpected exception but:

  1. Does not clear _in_flight_prompt_ids
  2. Does not emit "error" or "idle" status to WebSockets
  3. Frontend has no way to know the worker died

Suggested Fix

  1. In _agent_loop(): Add except MCPRuntimeError to gracefully handle MCP failures:
try:
    await self.wait_for_background_mcp_loading()
except MCPRuntimeError as e:
    logger.warning("MCP loading failed, continuing without MCP tools: {}", e)
finally:
    if loading:
        wire_send(StatusUpdate(mcp_status=self._mcp_status_snapshot()))
        wire_send(MCPLoadingEnd())
  1. In _read_loop(): On unexpected exceptions, also clear in-flight prompts and emit error status before exiting.

Related

  • The /web switch also has a subprocess cleanup issue: when preserve_background_tasks=True, MCP child processes from the TUI are not terminated, causing port conflicts for the Web UI worker.

Environment

  • kimi-cli version: latest (via uv tool upgrade kimi-cli --no-cache)
  • OS: macOS
  • Python: 3.13

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in kimi_cli/soul/kimisoul.py at _agent_loop() and inspect how wait_for_background_mcp_loading() handles MCPRuntimeError. Then read kimi_cli/web/runner/process.py at _read_loop() to trace prompt cleanup and WebSocket status handling. Done means an MCP connection failure leaves the worker usable without that server, while failed prompts receive appropriate cleanup and status updates.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, cli
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.