StreamableHTTPClientTransport cannot be restarted after close() — breaks OAuth re-authentication

オープン 初心者向け
#1,641 コメント 2 件 リアクション 0 件 担当者 0 名 GitHub で見る

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

評価

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

調査の方向性

src/client/streamableHttp.ts の StreamableHTTPClientTransport.start() と close() から始め、_abortController のライフサイクルを追跡します。終了時に transport が再起動可能な状態になり、OAuth の再認証シーケンスが already-started エラーなしで start() を呼び出せれば完了です。

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

説明

auth bug fix proposed P2 ready for work

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:

  1. transport.start() is called → sets this._abortController
  2. Server returns 401 → OAuth flow begins
  3. transport.close() is called → aborts _abortController but does NOT set it to undefined
  4. After OAuth completes, transport.start() is called again
  5. 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

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

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

はじめの一歩

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

modelcontextprotocol/typescript-sdk のほかの issue

modelcontextprotocol/typescript-sdk の issue をすべて見る

似ている issue

TypeScript の issue をもっと見る

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

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