MCP auth retry leaves orphaned transport/session when 401 occurs during initialize
まだ誰も着手していません。
評価
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 初心者へのやさしさ
- 76/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 活発
- 技術スタック
- python
- 領域
- api, authentication, backend
調査の方向性
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 _initialize → connect_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:
- Updates
Authorizationheaders - Calls
_initialize()again without closing the first ExitStack entries - 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
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
langgenius/dify のほかの issue
-
Annotation Reply: a stored score threshold of 0.0 is silently replaced with 1, disabling the feature オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
langgenius/dify#42639 · コメント 1 件 · リアクション 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
langgenius/dify#42468 · コメント 1 件 · リアクション 1 件 ·
-
🐞 bug
難易度 2/5 1〜3時間 初心者へのやさしさ 86/100
langgenius/dify#42446 · リアクション 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
langgenius/dify#42355 · コメント 1 件 · リアクション 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
langgenius/dify#42350 · コメント 1 件 · リアクション 1 件 ·
langgenius/dify の issue をすべて見る
似ている issue
-
calcite-components needs triage refactor
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
Esri/calcite-design-system#15203 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 91/100
-
community first-timers-only good first issue hacktoberfest help wanted low hanging fruit up-for-grabs
難易度 1/5 1時間未満 初心者へのやさしさ 95/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
Automattic/studio#4908 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100