Skip to content

feat: MCP 服务器手动重连功能#84

Open
dengmik-commits wants to merge 10 commits into
lessweb:mainfrom
dengmik-commits:main
Open

feat: MCP 服务器手动重连功能#84
dengmik-commits wants to merge 10 commits into
lessweb:mainfrom
dengmik-commits:main

Conversation

@dengmik-commits
Copy link
Copy Markdown
Contributor

概述

当前 MCP 服务器初始化失败后直接标记 failed,无法恢复。本 PR 增加了手动重连机制:用户可在 /mcp 界面进入失败的服务器详情,选择 Reconnect 触发单次重连。

设计

/mcp → 服务器列表
  fetch: ✗ Failed
  ↓ 按 Enter 进入详情
  > ↻ [Reconnect]        ← 二级菜单项
    Error: exited with code 7
  ↓ 按 Enter 触发重连
  fetch: ↻ Reconnecting...
  ↓ 成功 → ✓ Ready / 失败 → ✗ Failed

关键约束

  • 失败后不自动重试,由用户主动触发
  • 每次重连仅尝试一次,无 backoff/retry
  • 重连时实时读取磁盘上的最新 settings.json,无需重启

变更

src/mcp/mcp-client.ts

  • 新增 onDisconnect 回调参数,区分主动断开与进程崩溃
  • 新增 isConnected() 方法
  • safeReject 修复进程退出竞态条件:close 事件可能在 sendRequest 注册前触发,导致 connect() 永远等待超时
  • 适配上游的 Windows spawn 修复(直接传递 command+args,不强制拼 .cmd

src/mcp/mcp-manager.ts

  • McpServerStatus.status 类型新增 "reconnecting"
  • initialize() 拆分出 connectServer():单次尝试,失败直接 setStatus("failed")
  • 新增公开方法 reconnect(name, config?):接受可选的最新配置,更新缓存并重连
  • onServerCrash():运行时崩溃清理旧条目并标记 failed,不自动重连
  • 可配环境变量:DEEPCODE_MCP_TIMEOUT(单次连接超时,默认 30s)

src/session.ts

  • 新增 reconnectMcpServer(name, config?) 委托给 McpManager

src/ui/App.tsx

  • McpStatusList 传入 onReconnect 回调
  • 回调内调用 resolveCurrentSettings() 保证每次重连都读取最新配置

src/ui/McpStatusList.tsx

  • enterDetail 允许 failed / reconnecting 服务器进入详情
  • ServerDetailView 新增 onReconnect prop
  • failed 服务器渲染 [Reconnect] 菜单项,默认 > 光标选中
  • ItemRow 支持 action 类型,选中时显示 > 前缀
  • 底部提示:Enter to reconnect · Esc back · Ctrl+C close

src/tests/session.test.ts

  • SessionManager marks MCP server as failed on single failed attempt (no auto-retry) — 确认失败后无自动重连
  • SessionManager reconnect succeeds on previously failed server — 确认手动重连成功

验证

自动化测试

npm run typecheck    # ✅ 零错误
npm run bundle       # ✅ dist/cli.js 335KB
DEEPCODE_MCP_TIMEOUT=3000 npm test  # ✅ 208/213 pass

手动验证流程

1. 制造失败:~/.deepcode/settings.json 中 fetch 服务器包名改为不存在的包:

  "fetch": {
    "command": "npx",
-   "args": ["-y", "mcp-fetch-server"]
+   "args": ["-y", "mcp-fetch-server-BROKEN"]
  }

2. 启动 deepcode,执行 /mcp

Manage MCP servers (1 ready, 1 starting, 1 failed)
  ...
  > ✗ fetch               Failed
    Error: ... exited with code 1 ...

3. Enter 进入详情:

✗ fetch — Status
  Error: ... exited with code 1 ...
  ─────────────────
  > ↻ [Reconnect]

4. Enter 触发重连(包名仍然错误)—— 返回列表,看到 ↻ Reconnecting... 后再次 ✗ Failed

5. 改回正确包名:

- "args": ["-y", "mcp-fetch-server-BROKEN"]
+ "args": ["-y", "mcp-fetch-server"]

6. 再次 Enter → [Reconnect] → Enter —— fetch 恢复 ✓ Ready

关键: 步骤 5-6 无需重启 deepcode,reconnect() 每次从磁盘实时读取最新配置。

Lellansin and others added 3 commits May 16, 2026 15:20
- Add onProcessStdout callback chain through executor → bash-handler → session → App
- Stream real-time stdout/stderr from bash commands to UI ref (capped at 1MB)
- Add ProcessStdoutView fullscreen overlay with scroll support
- Bind Ctrl+O in PromptInput to toggle the stdout view
- Footer hint shows 'ctrl+o view output' when a process is running
Adds CLI argument -p / --prompt that takes a prompt string and
auto-submits it immediately when deepcode starts.

Usage:
  deepcode -p 'write a Python script'
  deepcode --prompt 'write a Python script'
Replace automatic retry with user-initiated reconnect:
- Failed servers show error details and a [Reconnect] option
- Reconnect reads latest config from disk (no restart needed)
- Single attempt per reconnect, no backoff/retry
@qorzj
Copy link
Copy Markdown
Collaborator

qorzj commented May 17, 2026

@dengmik-commits 高优问题:reconnect后界面会显示 MCP 服务已经重连成功,但实际对话时模型仍然用不到这个服务提供的工具。也就是说用户会看到“Ready”,但后续让模型调用该 MCP 工具时可能还是失败或根本不会被调用。

复现步骤:

  1. 把settings.json这mcp args设为-n,使MCP server不能成功启动:
"playwright": {
      "command": "npx",
      "args": [
        "-n",
        "@playwright/mcp@latest"
      ]
    }
image
  1. -n改成-y后,按照PR描述的步骤重连,此时/mcp显示"ready"状态
image
  1. 输入playwright相关的任务,但DeepCode并不会调用MCP工具
image

qorzj and others added 5 commits May 17, 2026 12:57
feat: Add Ctrl+O live process stdout viewer
… sync

Upstream v0.1.21 reverted PR lessweb#70. Re-apply:
- isShiftReturn() / isReturn() dynamic CSI modifier bit parsing
- Kitty progressive enhancement (ESC[>1u) alongside xterm modifyOtherKeys
- Clear input when key.return is true (safety net)
After reconnectMcpServer succeeds, SessionManager's cached
mcpToolDefinitions was stale, causing "Unknown MCP tool" errors
when the model tried to call reconnected tools.
@dengmik-commits
Copy link
Copy Markdown
Contributor Author

已修复。

根因:reconnectMcpServer() 重连成功后只更新了 McpManager 内部的 tools 列表,但没有同步刷新 SessionManager 层的 mcpToolDefinitions 缓存。所以 /mcp UI 显示 Ready,但模型调用时工具定义缓存是旧值,查找失败。

修复:加一行 this.mcpToolDefinitions = this.mcpManager.getMcpToolDefinitions(),和 initMcpServers 的逻辑一致。

 async reconnectMcpServer(name: string, config?: McpServerConfig): Promise<void> {
   await this.mcpManager.reconnect(name, config);
+  this.mcpToolDefinitions = this.mcpManager.getMcpToolDefinitions();
 }

请再测试一遍。

@qorzj
Copy link
Copy Markdown
Collaborator

qorzj commented May 18, 2026

@dengmik-commits 当前这个PR合并了 #70 的代码,但PR#70有阻塞性问题(#70 (comment) )。当前PR请只提交跟MCP相关的变更。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants