The reconnecting exemption keyed only on `_ever_connected`, so a server that
connected once and then parked on a PERMANENT error (revoked credentials,
endpoint gone: `_park_and_rearm('from parked state (permanent error)')`) looked
identical to a router-reboot park. Its self-probe fails the same way every
interval, so the job ran tool-less on every tick forever with only a gateway
WARNING and never received the one-shot blocked_config alert it used to get.
Record the revival reason `_park` was given on MCPServerTask (`_park_reason`,
cleared when a session proves healthy) and have `mcp_server_reconnecting`
return False for a permanent-error park, restoring the block and its alert.
Transient parks (network blip, rapid-drop budget exhausted) still return True.
Also dedupe the exemption's WARNING per job+server for the length of one
outage, like the one-shot blocked_config alert, instead of logging every tick;
the entry drops once the server resolves tools again so the next outage warns.
48 lines
1.8 KiB
Python
48 lines
1.8 KiB
Python
"""``mcp_server_reconnecting`` (cron preflight's "is this outage healing on its own?" question)
|
|
must say True only for a transient park. A server parked on a PERMANENT error (revoked
|
|
credentials, endpoint gone) fails its self-probe identically every time, so treating it as
|
|
reconnecting would run the job tool-less forever with no blocked_config alert.
|
|
"""
|
|
|
|
import asyncio
|
|
|
|
import pytest
|
|
|
|
import tools.mcp_tool as core
|
|
from tools.mcp_tool_discovery import mcp_server_reconnecting
|
|
|
|
|
|
@pytest.fixture
|
|
def parked_server(monkeypatch):
|
|
server = core.MCPServerTask("x")
|
|
server._ever_connected = True
|
|
server.session = None
|
|
server._task = None
|
|
monkeypatch.setitem(core._servers, "x", server)
|
|
return server
|
|
|
|
|
|
def test_transient_park_is_reconnecting(parked_server):
|
|
assert mcp_server_reconnecting("x") is True
|
|
parked_server._park_reason = "from parked state" # rapid-drop budget exhausted, still probing
|
|
assert mcp_server_reconnecting("x") is True
|
|
|
|
|
|
def test_permanent_error_park_is_not_reconnecting(parked_server):
|
|
parked_server._park_reason = "from parked state (permanent error)"
|
|
assert mcp_server_reconnecting("x") is False
|
|
|
|
|
|
def test_park_records_reason_and_proven_session_clears_it(parked_server, monkeypatch):
|
|
"""The run loop hands ``_park`` its revival reason; a proven healthy session forgets it."""
|
|
async def _shutdown_at_once(timeout=None):
|
|
return "shutdown"
|
|
monkeypatch.setattr(parked_server, "_wait_for_reconnect_or_shutdown", _shutdown_at_once)
|
|
|
|
asyncio.run(parked_server._park("from parked state (permanent error)"))
|
|
assert parked_server._park_reason == "from parked state (permanent error)"
|
|
assert mcp_server_reconnecting("x") is False
|
|
|
|
parked_server._mark_session_proven()
|
|
assert parked_server._park_reason is None
|