Просмотр исходного кода

fix(mcp): stop idle OAuth callback server (#32245)

Aiden Cline 2 месяцев назад
Родитель
Сommit
5d0f86606a

+ 11 - 0
packages/opencode/src/mcp/oauth-callback.ts

@@ -71,6 +71,13 @@ function cleanupStateIndex(oauthState: string) {
   }
 }
 
+function stopIfIdle() {
+  if (pendingAuths.size > 0 || !server) return
+
+  server.close()
+  server = undefined
+}
+
 function handleRequest(req: import("http").IncomingMessage, res: import("http").ServerResponse) {
   const url = new URL(req.url || "/", `http://localhost:${currentPort}`)
 
@@ -104,6 +111,7 @@ function handleRequest(req: import("http").IncomingMessage, res: import("http").
     }
     res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
     res.end(HTML_ERROR(errorMsg))
+    stopIfIdle()
     return
   }
 
@@ -130,6 +138,7 @@ function handleRequest(req: import("http").IncomingMessage, res: import("http").
 
   res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" })
   res.end(HTML_SUCCESS)
+  stopIfIdle()
 }
 
 export async function ensureRunning(redirectUri?: string): Promise<void> {
@@ -168,6 +177,7 @@ export function waitForCallback(oauthState: string, mcpName?: string): Promise<s
         pendingAuths.delete(oauthState)
         if (mcpName) mcpNameToState.delete(mcpName)
         reject(new Error("OAuth callback timeout - authorization took too long"))
+        stopIfIdle()
       }
     }, CALLBACK_TIMEOUT_MS)
 
@@ -185,6 +195,7 @@ export function cancelPending(mcpName: string): void {
     pendingAuths.delete(key)
     mcpNameToState.delete(mcpName)
     pending.reject(new Error("Authorization cancelled"))
+    stopIfIdle()
   }
 }
 

+ 12 - 0
packages/opencode/test/mcp/oauth-callback.test.ts

@@ -32,6 +32,18 @@ describe("McpOAuthCallback.ensureRunning", () => {
     expect(McpOAuthCallback.isRunning()).toBe(true)
   })
 
+  test("stops after the callback completes", async () => {
+    const redirectUri = "http://127.0.0.1:18003/custom/callback"
+    await McpOAuthCallback.ensureRunning(redirectUri)
+    const callback = McpOAuthCallback.waitForCallback("success")
+
+    const response = await fetch(`${redirectUri}?code=code&state=success`)
+
+    expect(response.status).toBe(200)
+    expect(await callback).toBe("code")
+    expect(McpOAuthCallback.isRunning()).toBe(false)
+  })
+
   test("escapes provider error markup in callback HTML", async () => {
     const redirectUri = "http://127.0.0.1:18001/custom/callback"
     await McpOAuthCallback.ensureRunning(redirectUri)