Skip to content

McpHub:MCP 伺服器生命週期管理

源码版本v4.0.10

職責

McpHub 是 Cline 接入外部 MCP 伺服器的中央樞紐。它負責讀取 cline_mcp_settings.json 裡設定的所有伺服器,為每個伺服器建立一條獨立的 client + transport 連線,然後把暴露出來的工具 (tool)、資源 (resource)、提示 (prompt) 快取到記憶體裡供主 agent 呼叫。整條 MCP 工具鏈——從設定變更、檔案監聽、連線/重連、能力發現、到 tools/call 呼叫——都收斂到這一個類,主 Task 不需要直接碰 MCP SDK。

它的位置在「主 agent loop」和「具體 MCP 協議」之間。想呼叫一個 MCP 工具時,Task 透過 McpHub.callTool (callTool:1233) 走出去;UI 想看當前伺服器列表,透過 getServers() 拿到一個排序後的快照 (getServers:112)。一個 Extension 實例裡只有一個 McpHub,但 connections: McpConnection[] 陣列裡每條都是獨立 transport,掛了不影響別的。

設計動機

  • 每伺服器一個 client:不同 MCP 伺服器有不同 capabilities、不同設定、不同錯誤處理,獨立 client 讓重連和作用域隔離都簡單 (per-server client:363)。
  • 統一三種 transport:stdio 跑本地行程、sse 走 Server-Sent Events、streamableHttp 用新版 HTTP 流,在 connectToServerswitch 裡分發 (transport switch:382)。
  • **檔案監聽 + 內部更新雙通道watchMcpSettingsFile 監聽外部編輯器改設定,isUpdatingClineSettings 標記內部自己寫時跳過觸發 (isUpdatingClineSettings:76),避免回環。
  • OAuth 按需懶載入:只有 sse/streamableHttp 才找 McpOAuthManager 要 authProvider,stdio 不走 (authProvider setup:377)。
  • 企業策略前置於連線:企業部署可以禁 marketplace、設白名單,在 stdio 連線前就攔截掉 (enterprise allowlist:310)。
  • UID 縮短工具名:每個伺服器分配一個 c + nanoid(5) 的短 uid,避免 MCP 工具名拼成長串 (getMcpServerKey:130)。

關鍵檔案

資料流

connectToServer 進來先做三件事:校驗企業策略、構造 disabled 占位或真實 client、選 transport。transport 選完建 McpConnection 推進陣列,然後 client.connect(transport) 真正握手。握手成功後才註冊通知回呼並拉四份能力清單:

typescript
// apps/vscode/src/services/mcp/McpHub.ts
connection.server.status = "connected"
connection.server.error = ""

// Register notification handler for real-time messages
try {
  // ...
  connection.client.setNotificationHandler(NotificationMessageSchema as any, async (notification: any) => {
    // ...
    if (this.notificationCallback) {
      this.notificationCallback(name, level, message)
    } else {
      this.pendingNotifications.push({ serverName: name, level, message, timestamp: Date.now() })
    }
  })
} catch (error) {
  Logger.error(`[MCP Debug] Error setting notification handlers for ${name}:`, error)
}

// Initial fetch of tools, resources, and prompts
connection.server.tools = await this.fetchToolsList(name)
connection.server.resources = await this.fetchResourcesList(name)
connection.server.resourceTemplates = await this.fetchResourceTemplatesList(name)
connection.server.prompts = await this.fetchPromptsList(name)

這段在 post-connect setup:584 附近。連線建好後,外部工具呼叫都走 callTool——查連線、按 config.timeout 算逾時、發 tools/call (tools/call request:1270)。資源讀類似,走 resources/read (resources/read:1183)。

邊界與失敗

  • OAuth 缺失即降級:client.connectUnauthorizedError 時,不報錯而是把 connection 標成 oauthRequired: true,等使用者在前端授權 (UnauthorizedError branch:556)。
  • disabled 伺服器占位但不連線:disabled 設定仍進 connections 陣列以便 UI 顯示,但 client/transport 都是 null (disabled placeholder:339)。
  • stdio stderr 分類:stderr 裡出現 error 字樣才記 error,否則當 info log (stderr classification:420)。
  • streamableHttp 404 當 405:很多伺服器把 SSE 不支援時該回 405 錯回 404,這裡在 fetch 層把 404 歸一成 405 讓 SDK 接受 (404 to 405:499)。
  • SSE token 動態刷新:sse 分支用自訂 fetch 每次現取 authProvider.tokens(),否則 token 過期後重連會一直 401 (dynamic token fetch:459)。
  • streamableHttp 重連交給 handler:StreamableHttpReconnectHandler 拿到 connectToServer 閉包,自己決定重連節奏 (reconnect handler:519)。
  • 通知暫存:沒有活動 Task 時,伺服器來的 notification 先存 pendingNotifications,等下一個 Task 起來再取 (pendingNotifications:631)。

小結

McpHub 把 MCP 協議細節全收進自己,對主 agent 暴露 callTool/readResource/getServers 三個口子。想看它如何被工具層包裝成 use_mcp_tool 可以讀 cap-tools/use-mcp-tool;想看 subagent 如何隔離自己的上下文視窗可以讀 subagent;想看主 Task 怎麼把 MCP 伺服器列表塞進系統提示可以讀 agent-loop/task-class

對照官方資料:Cline 文件 · README