@@ -259,7 +259,8 @@ describe("HTTP MCP auth policy", () => {
259259 ) ;
260260 while ( tokenExchangeSignals . length === 0 ) await Promise . resolve ( ) ;
261261
262- expect ( tokenExchangeSignals [ 0 ] ) . toBe ( abort . signal ) ;
262+ expect ( tokenExchangeSignals [ 0 ] ) . toBeDefined ( ) ;
263+ expect ( tokenExchangeSignals [ 0 ] ?. aborted ) . toBe ( false ) ;
263264 abort . abort ( new Error ( "toolset disposed" ) ) ;
264265 const result = await connection ;
265266
@@ -280,12 +281,17 @@ describe("HTTP MCP auth policy", () => {
280281 expect ( result . ok ) . toBe ( true ) ;
281282 if ( ! result . ok ) return ;
282283 expect ( transportOptions ) . toEqual ( [
283- { authProvider, requestInit : { signal : abort . signal } , fetch : expect . any ( Function ) } ,
284+ {
285+ authProvider,
286+ requestInit : { signal : expect . any ( AbortSignal ) } ,
287+ fetch : expect . any ( Function ) ,
288+ } ,
284289 ] ) ;
285290
286291 const call = result . client . call ( "ping" , { } , abort . signal ) ;
287292 while ( tokenRefreshSignals . length === 0 ) await Promise . resolve ( ) ;
288- expect ( tokenRefreshSignals [ 0 ] ) . toBe ( abort . signal ) ;
293+ expect ( tokenRefreshSignals [ 0 ] ) . toBeDefined ( ) ;
294+ expect ( tokenRefreshSignals [ 0 ] ?. aborted ) . toBe ( false ) ;
289295 abort . abort ( new Error ( "toolset disposed" ) ) ;
290296 await expect ( call ) . rejects . toThrow ( "toolset disposed" ) ;
291297 expect ( tokenRefreshAborts ) . toBe ( 1 ) ;
@@ -310,7 +316,49 @@ describe("HTTP MCP auth policy", () => {
310316 expect ( callbackStarts ) . toBe ( 1 ) ;
311317 expect ( providerCreates ) . toBe ( 1 ) ;
312318 expect ( providerServerURL ) . toBe ( "https://custom.example/mcp?mode=full" ) ;
313- expect ( transportOptions ) . toEqual ( [ { authProvider } ] ) ;
319+ expect ( transportOptions ) . toEqual ( [
320+ {
321+ authProvider,
322+ requestInit : { signal : expect . any ( AbortSignal ) } ,
323+ fetch : expect . any ( Function ) ,
324+ } ,
325+ ] ) ;
326+ } ) ;
327+
328+ test ( "client close aborts in-flight OAuth without aborting the connect signal" , async ( ) => {
329+ const connectAbort = new AbortController ( ) ;
330+ const result = await connectMCPServer (
331+ { name : "linear" , type : "http" , url : "https://mcp.linear.app/mcp" } ,
332+ { signal : connectAbort . signal } ,
333+ ) ;
334+ expect ( result . ok ) . toBe ( true ) ;
335+ if ( ! result . ok ) return ;
336+
337+ const transport = transportOptions [ 0 ] as {
338+ requestInit ?: { signal ?: AbortSignal } ;
339+ fetch ?: ( url : string | URL , init ?: RequestInit ) => Promise < Response > ;
340+ } ;
341+ expect ( transport . requestInit ?. signal ) . toBeDefined ( ) ;
342+ expect ( transport . requestInit ?. signal ) . not . toBe ( connectAbort . signal ) ;
343+ expect ( transport . fetch ) . toBeTypeOf ( "function" ) ;
344+
345+ const call = result . client . call ( "ping" , { } , new AbortController ( ) . signal ) ;
346+ while ( tokenRefreshSignals . length === 0 ) await Promise . resolve ( ) ;
347+ expect ( tokenRefreshSignals [ 0 ] ) . not . toBe ( connectAbort . signal ) ;
348+ expect ( tokenRefreshSignals [ 0 ] ?. aborted ) . toBe ( false ) ;
349+ expect ( connectAbort . signal . aborted ) . toBe ( false ) ;
350+
351+ await result . client . close ( ) ;
352+
353+ await expect ( call ) . rejects . toThrow ( ) ;
354+ expect ( tokenRefreshAborts ) . toBe ( 1 ) ;
355+ expect ( connectAbort . signal . aborted ) . toBe ( false ) ;
356+ expect ( transport . requestInit ?. signal ?. aborted ) . toBe ( true ) ;
357+ const fetchFn = transport . fetch ;
358+ expect ( fetchFn ) . toBeTypeOf ( "function" ) ;
359+ if ( fetchFn === undefined ) return ;
360+ await expect ( fetchFn ( "https://auth.test/token" ) ) . rejects . toThrow ( ) ;
361+ expect ( connectAbort . signal . aborted ) . toBe ( false ) ;
314362 } ) ;
315363} ) ;
316364
0 commit comments