Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions scripts/release.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"128259593+Gutslabs@users.noreply.github.com": "Gutslabs",
"50326054+nocturnum91@users.noreply.github.com": "nocturnum91",
"abdielv@proton.me": "AJV20",
"mason@growagainorchids.com": "masonjames",
"159539633+MottledShadow@users.noreply.github.com": "MottledShadow",
"aludwin+gh@gmail.com": "adamludwin",
"ngusev@astralinux.ru": "NikolayGusev-astra",
Expand Down
37 changes: 37 additions & 0 deletions tests/tools/test_mcp_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,43 @@ def _interrupt_soon():
mcp_mod._mcp_loop = old_loop
mcp_mod._mcp_thread = old_thread

def test_timeout_reports_elapsed_and_configured_timeout(self):
import tools.mcp_tool as mcp_mod

loop = asyncio.new_event_loop()
thread = threading.Thread(target=loop.run_forever, daemon=True)
thread.start()

cancelled = threading.Event()

async def _slow_call():
try:
await asyncio.sleep(5)
return "done"
except asyncio.CancelledError:
cancelled.set()
raise

old_loop = mcp_mod._mcp_loop
old_thread = mcp_mod._mcp_thread
mcp_mod._mcp_loop = loop
mcp_mod._mcp_thread = thread

try:
with pytest.raises(TimeoutError, match=r"MCP call timed out after .*configured timeout: 0.2s"):
mcp_mod._run_on_mcp_loop(_slow_call(), timeout=0.2)

deadline = time.time() + 2
while time.time() < deadline and not cancelled.is_set():
time.sleep(0.05)
assert cancelled.is_set()
finally:
loop.call_soon_threadsafe(loop.stop)
thread.join(timeout=2)
loop.close()
mcp_mod._mcp_loop = old_loop
mcp_mod._mcp_thread = old_thread


# ---------------------------------------------------------------------------
# Tool registration (discovery + register)
Expand Down
10 changes: 8 additions & 2 deletions tools/mcp_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -1942,7 +1942,8 @@ def _run_on_mcp_loop(coro, timeout: float = 30):
if loop is None or not loop.is_running():
raise RuntimeError("MCP event loop is not running")
future = asyncio.run_coroutine_threadsafe(coro, loop)
deadline = None if timeout is None else time.monotonic() + timeout
start_time = time.monotonic()
deadline = None if timeout is None else start_time + timeout

while True:
if is_interrupted():
Expand All @@ -1953,7 +1954,12 @@ def _run_on_mcp_loop(coro, timeout: float = 30):
if deadline is not None:
remaining = deadline - time.monotonic()
if remaining <= 0:
return future.result(timeout=0)
future.cancel()
elapsed = time.monotonic() - start_time
raise TimeoutError(
f"MCP call timed out after {elapsed:.1f}s "
f"(configured timeout: {float(timeout):.1f}s)"
)
wait_timeout = min(wait_timeout, remaining)

try:
Expand Down
Loading