StreamableHTTPClientTransport cannot be restarted after close() — breaks OAuth re-authentication
まだ誰も着手していません。
評価
- 難易度
- 2/5
- 見積もり時間
- 1〜3時間
- 初心者へのやさしさ
- 72/100
- issue の種類
- バグ
- 明瞭さ
- 明確に書かれている
- 活発さ
- 静か
- 技術スタック
- typescript
- 領域
- api, authentication
調査の方向性
src/client/streamableHttp.ts の StreamableHTTPClientTransport.start() と close() から始め、_abortController のライフサイクルを追跡します。終了時に transport が再起動可能な状態になり、OAuth の再認証シーケンスが already-started エラーなしで start() を呼び出せれば完了です。
索引モデルが issue の本文から書いたものです。
説明
Bug Description
StreamableHTTPClientTransport.start() throws "StreamableHTTPClientTransport already started!" if called after close(), because close() aborts the _abortController but never resets it to undefined.
This breaks any flow that needs to reconnect after OAuth authentication, because:
transport.start()is called → setsthis._abortController- Server returns 401 → OAuth flow begins
transport.close()is called → aborts_abortControllerbut does NOT set it toundefined- After OAuth completes,
transport.start()is called again - The guard at line 257 checks
if (this._abortController)→ still truthy → throws
Reproduction
Any MCP client that uses StreamableHTTPClientTransport with an OAuth auth provider hitting a server that requires authentication will fail on the first connection attempt. The token is saved successfully, but the transport cannot be restarted in the same process.
This is observable with tools like mcporter when connecting to an OAuth-protected MCP server for the first time.
Root Cause
In src/client/streamableHttp.ts, the close() method aborts the controller but doesn't clear the reference:
async close(): Promise<void> {
// ...
this._abortController?.abort(); // aborts but keeps reference
this.onclose?.();
}
While start() guards against re-entry by checking if _abortController exists:
async start(): Promise<void> {
if (this._abortController) {
throw new Error('StreamableHTTPClientTransport already started!...');
}
this._abortController = new AbortController();
}
Suggested Fix
Reset _abortController to undefined in close():
async close(): Promise<void> {
// ...
this._abortController?.abort();
this._abortController = undefined;
this.onclose?.();
}
This allows the transport to be restarted after being closed, which is the expected lifecycle for OAuth re-authentication flows.
Environment
@modelcontextprotocol/sdk: 1.27.1- Runtime: Node.js
- Transport: StreamableHTTPClientTransport
- 主要言語
- TypeScript
- スター
- 13.4k
- フォーク
- 2.2k
- 平均マージ
- 3日 12時間
- マージ済み PR(30日)
- 3
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
modelcontextprotocol/typescript-sdk のほかの issue
-
Auth metadata discovery: fallback URL built on resource host instead of authorization-server host オープン
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
-
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
modelcontextprotocol/typescript-sdk#2783 · コメント 1 件 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
modelcontextprotocol/typescript-sdk#2766 · コメント 1 件 ·
-
難易度 2/5 1〜2日 初心者へのやさしさ 72/100
modelcontextprotocol/typescript-sdk の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 84/100
Eynzof/Hermes-CN-Desktop#610 ·
-
bug clawsweeper:linked-pr-open clawsweeper:needs-live-repro clawsweeper:no-new-fix-pr impact:message-loss issue-rating: 🐚 platinum hermit P2 regression
難易度 2/5 1〜3時間 初心者へのやさしさ 78/100
-
enhancement
難易度 2/5 1〜3時間 初心者へのやさしさ 68/100
-
calcite-components needs triage refactor
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
Esri/calcite-design-system#15203 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 90/100
danielmiessler/LifeOS#2218 ·