From 1247ff2dca0dbc68957ee4ad153aa34f165a184d Mon Sep 17 00:00:00 2001 From: AsoTora <36507055+AsoTora@users.noreply.github.com> Date: Thu, 14 May 2026 07:58:37 -0700 Subject: [PATCH] fix: stop retrying initial MCP auth failures --- tests/tools/test_mcp_tool.py | 34 ++++++++++++++++++++++++++++++++++ tools/mcp_tool.py | 10 ++++++++++ 2 files changed, 44 insertions(+) diff --git a/tests/tools/test_mcp_tool.py b/tests/tools/test_mcp_tool.py index a10c7f43616..5558a0df48c 100644 --- a/tests/tools/test_mcp_tool.py +++ b/tests/tools/test_mcp_tool.py @@ -1592,6 +1592,40 @@ class TestReconnection: asyncio.run(_test()) + def test_initial_oauth_failure_does_not_retry(self): + """Initial OAuth failures stop immediately to avoid repeated browser prompts.""" + from tools.mcp_tool import MCPServerTask + + run_count = 0 + target_server = None + oauth_error = RuntimeError("Token exchange failed (400): Unknown client_id") + + original_run_stdio = MCPServerTask._run_stdio + + async def patched_run_stdio(self_srv, config): + nonlocal run_count, target_server + run_count += 1 + if target_server is not self_srv: + return await original_run_stdio(self_srv, config) + raise oauth_error + + async def _test(): + nonlocal target_server + server = MCPServerTask("oauth_srv") + target_server = server + + with patch.object(MCPServerTask, "_run_stdio", patched_run_stdio), \ + patch("tools.mcp_tool._is_auth_error", return_value=True), \ + patch("asyncio.sleep", new_callable=AsyncMock) as mock_sleep: + await server.run({"command": "test"}) + + assert run_count == 1 + assert server._error is oauth_error + assert server._ready.is_set() + assert mock_sleep.await_count == 0 + + asyncio.run(_test()) + # --------------------------------------------------------------------------- # Configurable timeouts diff --git a/tools/mcp_tool.py b/tools/mcp_tool.py index 1e10b276f1e..ee1843043dc 100644 --- a/tools/mcp_tool.py +++ b/tools/mcp_tool.py @@ -1499,6 +1499,16 @@ class MCPServerTask: # should not permanently kill the server. # (Ported from Kilo Code's MCP resilience fix.) if not self._ready.is_set(): + if _is_auth_error(exc): + logger.warning( + "MCP server '%s' failed initial OAuth authentication, " + "not retrying automatically: %s", + self.name, exc, + ) + self._error = exc + self._ready.set() + return + initial_retries += 1 if initial_retries > _MAX_INITIAL_CONNECT_RETRIES: logger.warning(