From a23c4908c446f89ea24e055a727fa9701d6c8270 Mon Sep 17 00:00:00 2001 From: Beon de Nood Date: Thu, 21 May 2026 12:25:37 -0400 Subject: [PATCH 1/3] fix: guard_sync wraps TimeoutError as GuardError with descriptive message TimeoutError from concurrent.futures was leaking to users when guard_sync was called outside MCP context. Now catches it and raises GuardError with a helpful message explaining the likely cause. --- capiscio_mcp/guard.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/capiscio_mcp/guard.py b/capiscio_mcp/guard.py index 215cca6..f9681ac 100644 --- a/capiscio_mcp/guard.py +++ b/capiscio_mcp/guard.py @@ -688,7 +688,17 @@ async def run_eval(): # We're in an async context, use run_coroutine_threadsafe import concurrent.futures future = asyncio.run_coroutine_threadsafe(run_eval(), loop) - result = future.result(timeout=30.0) + try: + result = future.result(timeout=30.0) + except (TimeoutError, concurrent.futures.TimeoutError): + raise GuardError( + reason=DenyReason.INTERNAL_ERROR, + detail=( + "Guard evaluation timed out. This usually means the " + "guard is being called outside of an MCP request context " + "or the gRPC core server is not responding." + ), + ) else: # No event loop, create one result = asyncio.run(run_eval()) From 01bfe202c9d3ba979d7264e44d3408c5a1dd5206 Mon Sep 17 00:00:00 2001 From: Beon de Nood Date: Thu, 21 May 2026 12:40:21 -0400 Subject: [PATCH 2/3] chore: bump version to 2.7.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 3ecccce..bb7b9ab 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "capiscio-mcp" -version = "2.7.1" +version = "2.7.2" description = "Trust badges for MCP tool calls - RFC-006 & RFC-007 implementation" readme = "README.md" requires-python = ">=3.10" From b36cbeba7d17e681b1a6c9ca0fe4cfc93da14ae2 Mon Sep 17 00:00:00 2001 From: Beon de Nood Date: Thu, 21 May 2026 12:43:46 -0400 Subject: [PATCH 3/3] fix: cancel pending future on timeout to prevent resource leak --- capiscio_mcp/guard.py | 1 + 1 file changed, 1 insertion(+) diff --git a/capiscio_mcp/guard.py b/capiscio_mcp/guard.py index f9681ac..eb54054 100644 --- a/capiscio_mcp/guard.py +++ b/capiscio_mcp/guard.py @@ -691,6 +691,7 @@ async def run_eval(): try: result = future.result(timeout=30.0) except (TimeoutError, concurrent.futures.TimeoutError): + future.cancel() raise GuardError( reason=DenyReason.INTERNAL_ERROR, detail=(