MCP auth retry leaves orphaned transport/session when 401 occurs during initialize

オープン
#42,384 コメント 1 件 リアクション 1 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
3/5
見積もり時間
1〜2日
初心者へのやさしさ
76/100
issue の種類
バグ
明瞭さ
明確に書かれている
活発さ
活発
技術スタック
python

調査の方向性

Start in api/core/mcp/auth_client.py, tracing _execute_with_retry, enter, _initialize, and connect_server, then inspect the repro assertion for the partial-session case. Verify that a 401 during initialization closes the existing ExitStack and clears session state before retrying, while the fully initialized path still retries correctly.

索引モデルが issue の本文から書いたものです。

説明

Problem

MCPClientWithAuthRetry._execute_with_retry only tears down the MCP transport/ClientSession ExitStack when self._initialized is already True.

MCPClient.__enter__ sets _initialized = True after _initialize() returns. Inside _initializeconnect_server, the streamable-HTTP/SSE transport and ClientSession are already pushed onto _exit_stack before ClientSession.initialize() runs. A 401 from that handshake raises MCPAuthError while _initialized is still False.

On auth refresh + retry, the client therefore:

  1. Updates Authorization headers
  2. Calls _initialize() again without closing the first ExitStack entries
  3. Opens a second transport + session while the expired-token connection stays alive until the outer context exits

Why this matters

  • Core MCP OAuth / session-binding path used by tool list + invoke
  • Leaks concurrent MCP sessions (servers that bind one session per client can reject or confuse the second connect)
  • Stale connection with the old token remains open alongside the refreshed one

Evidence

# Current gate in api/core/mcp/auth_client.py
if self._initialized:
    self._exit_stack.close()
    self._session = None
    self._initialized = False
    self._initialize()
    self._initialized = True

Repro unit assertion (fails on main): after MCPAuthError with _initialized is False and a partial _session already attached, _exit_stack.close() is never called before the retry.

Proposed fix

Always close _exit_stack, clear _session, reset _initialized, and replace the ExitStack before retrying. Only call _initialize() again when the client was already fully initialized (list_tools / invoke path). The __enter__ retry path re-enters and initializes itself.

Related (not the same root cause)

  • #41499 / #41482 — orphaned JSON-RPC responses crashing the receive loop (base_session.py)
  • #42326 / #42327 — invoke skipping OAuth when custom headers are set (mcp_tool/tool.py)
主要言語
TypeScript
スター
157k
フォーク
24.7k
平均マージ
22時間 32分
マージ済み PR(30日)
611

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

langgenius/dify のほかの issue

langgenius/dify の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。