MoonshotAI / MoonshotAI/kimi-cli
MCP connection failure crashes Web UI worker instead of graceful degradation
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
- Configure an MCP server that uses a fixed port (e.g.,
chrome-local-bridgeon port 10086) - Open a first kimi-cli TUI window — the MCP server starts successfully
- In the same session, execute
/webto switch to Web UI - Send a message in Web UI
- 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
MCPRuntimeErroris thrown, the worker process crashes- 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()throwsMCPRuntimeError- Exception propagates uncaught through
_agent_loop() - Entire worker process exits
- WebSocket
_read_loopdies 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:
- Does not clear
_in_flight_prompt_ids - Does not emit
"error"or"idle"status to WebSockets - Frontend has no way to know the worker died
Suggested Fix
- In
_agent_loop(): Addexcept MCPRuntimeErrorto 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())
- In
_read_loop(): On unexpected exceptions, also clear in-flight prompts and emit error status before exiting.
Related
- The
/webswitch also has a subprocess cleanup issue: whenpreserve_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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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