From 86447064cada4306121fee17e42939165bf29b4e Mon Sep 17 00:00:00 2001 From: chinesepowered Date: Sat, 4 Apr 2026 14:50:02 -0700 Subject: [PATCH] fix(mcp): clear OAuth callback timeout on all completion paths The 5-minute timeout set in startCallbackServer was never cleared when the OAuth flow completed. The timer would fire later, calling reject() on a settled promise and server.close() on an already-closed server. Now clears the timeout before every resolve() and reject() call. --- packages/core/src/mcp/oauth-provider.ts | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/core/src/mcp/oauth-provider.ts b/packages/core/src/mcp/oauth-provider.ts index a2fca6eec..a3e998bb7 100644 --- a/packages/core/src/mcp/oauth-provider.ts +++ b/packages/core/src/mcp/oauth-provider.ts @@ -261,6 +261,10 @@ export class MCPOAuthProvider { `); activeCallbackServer = null; + if (activeCallbackTimeout) { + clearTimeout(activeCallbackTimeout); + activeCallbackTimeout = null; + } server.close(); reject(new Error(`OAuth error: ${error}`)); return; @@ -276,6 +280,10 @@ export class MCPOAuthProvider { res.writeHead(400); res.end('Invalid state parameter'); activeCallbackServer = null; + if (activeCallbackTimeout) { + clearTimeout(activeCallbackTimeout); + activeCallbackTimeout = null; + } server.close(); reject(new Error('State mismatch - possible CSRF attack')); return; @@ -294,10 +302,18 @@ export class MCPOAuthProvider { `); activeCallbackServer = null; + if (activeCallbackTimeout) { + clearTimeout(activeCallbackTimeout); + activeCallbackTimeout = null; + } server.close(); resolve({ code, state }); } catch (error) { activeCallbackServer = null; + if (activeCallbackTimeout) { + clearTimeout(activeCallbackTimeout); + activeCallbackTimeout = null; + } server.close(); reject(error); }