@@ -150,6 +150,27 @@ export namespace MCP {
150150 // Store transports for OAuth servers to allow finishing auth
151151 type TransportWithAuth = StreamableHTTPClientTransport | SSEClientTransport
152152 const pendingOAuthTransports = new Map < string , TransportWithAuth > ( )
153+ const pendingOAuthTimers = new Map < string , ReturnType < typeof setTimeout > > ( )
154+ const OAUTH_TRANSPORT_TTL_MS = 5 * 60 * 1_000 // 5 minutes
155+
156+ function setPendingOAuthTransport ( key : string , transport : TransportWithAuth ) {
157+ const existing = pendingOAuthTimers . get ( key )
158+ if ( existing ) clearTimeout ( existing )
159+ pendingOAuthTransports . set ( key , transport )
160+ const timer = setTimeout ( ( ) => {
161+ pendingOAuthTransports . delete ( key )
162+ pendingOAuthTimers . delete ( key )
163+ log . info ( "evicted stale pending OAuth transport" , { key } )
164+ } , OAUTH_TRANSPORT_TTL_MS )
165+ pendingOAuthTimers . set ( key , timer )
166+ }
167+
168+ function deletePendingOAuthTransport ( key : string ) {
169+ const timer = pendingOAuthTimers . get ( key )
170+ if ( timer ) clearTimeout ( timer )
171+ pendingOAuthTimers . delete ( key )
172+ pendingOAuthTransports . delete ( key )
173+ }
153174
154175 // Prompt cache types
155176 type PromptInfo = Awaited < ReturnType < MCPClient [ "listPrompts" ] > > [ "prompts" ] [ number ]
@@ -205,6 +226,8 @@ export namespace MCP {
205226 } ) ,
206227 ) ,
207228 )
229+ for ( const timer of pendingOAuthTimers . values ( ) ) clearTimeout ( timer )
230+ pendingOAuthTimers . clear ( )
208231 pendingOAuthTransports . clear ( )
209232 } ,
210233 )
@@ -378,7 +401,7 @@ export namespace MCP {
378401 } ) . catch ( ( e ) => log . debug ( "failed to show toast" , { error : e } ) )
379402 } else {
380403 // Store transport for later finishAuth call
381- pendingOAuthTransports . set ( key , transport )
404+ setPendingOAuthTransport ( key , transport )
382405 status = { status : "needs_auth" as const }
383406 // Show toast for needs_auth
384407 Bus . publish ( TuiEvent . ToastShow , {
@@ -772,7 +795,7 @@ export namespace MCP {
772795 } catch ( error ) {
773796 if ( error instanceof UnauthorizedError && capturedUrl ) {
774797 // Store transport for finishAuth
775- pendingOAuthTransports . set ( mcpName , transport )
798+ setPendingOAuthTransport ( mcpName , transport )
776799 return { authorizationUrl : capturedUrl . toString ( ) }
777800 }
778801 throw error
@@ -879,7 +902,7 @@ export namespace MCP {
879902 }
880903
881904 // Re-add the MCP server to establish connection
882- pendingOAuthTransports . delete ( mcpName )
905+ deletePendingOAuthTransport ( mcpName )
883906 const result = await add ( mcpName , mcpConfig )
884907
885908 const statusRecord = result . status as Record < string , Status >
@@ -899,7 +922,7 @@ export namespace MCP {
899922 export async function removeAuth ( mcpName : string ) : Promise < void > {
900923 await McpAuth . remove ( mcpName )
901924 McpOAuthCallback . cancelPending ( mcpName )
902- pendingOAuthTransports . delete ( mcpName )
925+ deletePendingOAuthTransport ( mcpName )
903926 await McpAuth . clearOAuthState ( mcpName )
904927 log . info ( "removed oauth credentials" , { mcpName } )
905928 }
0 commit comments