McpHub:MCP サーバーライフサイクル管理
役割
McpHub は Cline が外部 MCP (Model Context Protocol) サーバーを接続するための中央ハブ (McpHub) である。cline_mcp_settings.json に設定されたすべてのサーバーを読み込み、各サーバーに対して独立した client + transport 接続を確立し、公開されたツール (tool)、リソース (resource)、プロンプト (prompt) をメモリにキャッシュして主 agent が呼べるようにする。MCP ツールチェーン全体——設定変更、ファイル監視、接続/再接続、能力発見、tools/call 呼び出しまで——をこの 1 つのクラスに集約し、主 Task は MCP SDK を直接触る必要がない。
位置としては「主 agent loop」と「具体的 MCP プロトコル」の中間にある。Task が MCP ツールを呼ぶ時、McpHub.callTool (callTool:1233) を経由する。UI が現在のサーバーリストを見たい時は getServers() でソート済みスナップショットを取る (getServers:112)。Extension インスタンス内に McpHub は 1 つだけだが、connections: McpConnection[] 配列内の各エントリは独立 transport で、1 つ落ちても他へ影響しない。
設計動機
- サーバーごとに 1 つの client:異なる MCP サーバーは異なる capabilities、異なる設定、異なるエラー処理を持つ。独立 client により再接続とスコープ分離がシンプルになる (
per-server client:363)。 - 3 種 transport の統一:
stdioはローカルプロセス、sseは Server-Sent Events、streamableHttpは新版 HTTP ストリーム。connectToServerのswitchで振り分ける (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)。
主要ファイル
McpHub class:51— シングルトンクラス。connections、ファイル監視、OAuth manager、通知コールバックを保持。constructor:108— 起動時に即座にwatchMcpSettingsFile+initializeMcpServersを実行。watchMcpSettingsFile:213— chokidar で設定ファイル変更を監視し、diff から追加/削除/変更を検出して再接続。connectToServer:286— 中核接続メソッド。検証、transport 選択、client 構築、能力発見。transport switch:382— stdio / sse / streamableHttp の 3 分岐。stdio stderr pipe:415— stdio サーバーは stderr を引き出して info/error ログストリームにする。fetchToolsList:677—tools/listを送信し、各 tool にautoApproveマークを付ける。fetchResourcesList:713—resources/listを送信。disabled サーバーは空を返す。notification handler:611—notifications/messageコールバックを登録し、サーバーログを現在の Task または一時保留に流す。deleteConnection:784— transport と client を閉じ、connections からフィルタ除去。restartConnection:1047— サーバーを再接続。500ms の意図的遅延で UI が「connecting」状態を表示できるようにする。callTool:1233—tools/callを送信。サーバー設定からタイムアウトを取り、全程に telemetry を埋め込む。
データフロー
connectToServer に入ったらまず 3 つを行う。企業ポリシーの検証、disabled プレースホルダーまたは実 client の構築、transport 選択である。transport 選択後 McpConnection を作って配列に追加し、client.connect(transport) で実際にハンドシェイクする。ハンドシェイク成功後に通知コールバックを登録し、4 つの能力リストを取得する:
// 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.connectがUnauthorizedErrorを投げた時、エラーにせず connection をoauthRequired: trueとマークし、ユーザーがフロントエンドで認可するのを待つ (UnauthorizedError branch:556)。 - disabled サーバーはプレースホルダーのみで接続しない:disabled 設定は UI 表示のため connections 配列に入るが、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 の 3 つの口を露出する。これがどうツールレイヤーに包まれて use_mcp_tool になるかは cap-tools/use-mcp-tool、subagent がどう独自のコンテキストウィンドウを隔離するかは subagent、主 Task がどう MCP サーバーリストをシステムプロンプトに詰め込むかは agent-loop/task-class を参照のこと。