diff --git a/packages/coding-agent/src/modes/controllers/mcp-command-controller.ts b/packages/coding-agent/src/modes/controllers/mcp-command-controller.ts index 8f26e9740..cfbef3400 100644 --- a/packages/coding-agent/src/modes/controllers/mcp-command-controller.ts +++ b/packages/coding-agent/src/modes/controllers/mcp-command-controller.ts @@ -597,6 +597,11 @@ export class MCPCommandController { const resolvedClientSecret = clientSecret.trim() || undefined; const manualInput = this.ctx.oauthManualInput; + if (manualInput.hasPending() && manualInput.pendingProviderId !== MCP_MANUAL_INPUT_PROVIDER_ID) { + throw new Error( + `OAuth login already in progress for ${manualInput.pendingProviderId}. Complete or cancel it before starting MCP OAuth.`, + ); + } const oauthTimeout = new AbortController(); try { // Create OAuth flow @@ -652,8 +657,15 @@ export class MCPCommandController { onProgress: (message: string) => { this.ctx.present([new Spacer(1), new Text(theme.fg("muted", message), 1, 0)]); }, - onManualCodeInput: () => manualInput.waitForInput(MCP_MANUAL_INPUT_PROVIDER_ID), - signal: oauthTimeout.signal, + onManualCodeInput: () => { + const pendingInput = manualInput.tryWaitForInput(MCP_MANUAL_INPUT_PROVIDER_ID); + if (!pendingInput) { + throw new Error( + `OAuth login already in progress for ${manualInput.pendingProviderId}. Complete or cancel it before starting MCP OAuth.`, + ); + } + return pendingInput; + }, }, ); diff --git a/packages/coding-agent/src/modes/oauth-manual-input.ts b/packages/coding-agent/src/modes/oauth-manual-input.ts index aa0d0b1b8..4591fcb1d 100644 --- a/packages/coding-agent/src/modes/oauth-manual-input.ts +++ b/packages/coding-agent/src/modes/oauth-manual-input.ts @@ -17,6 +17,11 @@ export class OAuthManualInputManager { return promise; } + tryWaitForInput(providerId: string): Promise | undefined { + if (this.#pending) return undefined; + return this.waitForInput(providerId); + } + submit(input: string): boolean { if (!this.#pending) return false; const { resolve } = this.#pending; diff --git a/packages/coding-agent/test/oauth-manual-input.test.ts b/packages/coding-agent/test/oauth-manual-input.test.ts index 164a61934..89dcc2653 100644 --- a/packages/coding-agent/test/oauth-manual-input.test.ts +++ b/packages/coding-agent/test/oauth-manual-input.test.ts @@ -13,6 +13,17 @@ describe("OAuthManualInputManager", () => { expect(manager.hasPending()).toBe(false); }); + it("does not replace pending input when using tryWaitForInput", async () => { + const manager = new OAuthManualInputManager(); + const first = manager.waitForInput("openai-codex"); + + expect(manager.tryWaitForInput("mcp")).toBeUndefined(); + expect(manager.pendingProviderId).toBe("openai-codex"); + + expect(manager.submit("callback-url")).toBe(true); + expect(await first).toBe("callback-url"); + }); + it("returns false when no pending input", () => { const manager = new OAuthManualInputManager();