From 48ccc2aff3faba42d3beeed9ed58a08c867e2b4d Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Mon, 2 Feb 2026 14:25:07 -0700 Subject: [PATCH] fix: non-blocking health checks and wait defaults - docker_health now runs HTTP call in thread executor instead of blocking the async event loop (prevents MCP server freeze during polls) - docker_auto_start defaults to wait=False so tool returns immediately (clients should call docker_wait separately if needed) --- src/ghydramcp/mixins/docker.py | 39 +++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/ghydramcp/mixins/docker.py b/src/ghydramcp/mixins/docker.py index e5cae89..ded88de 100644 --- a/src/ghydramcp/mixins/docker.py +++ b/src/ghydramcp/mixins/docker.py @@ -815,21 +815,15 @@ class DockerMixin(MCPMixin): except subprocess.CalledProcessError as e: return {"error": f"Build failed: {e.stderr or e.stdout}"} - @mcp_tool( - name="docker_health", - description="Check if a GhydraMCP container's API is responding", - ) - async def docker_health( - self, port: int = 8192, timeout: float = 5.0, ctx: Optional[Context] = None - ) -> Dict[str, Any]: - """Check if a GhydraMCP container's API is healthy. + def _sync_health_check(self, port: int, timeout: float) -> Dict[str, Any]: + """Synchronous health check (runs in thread to avoid blocking event loop). Args: - port: API port to check (default: 8192) + port: API port to check timeout: Request timeout in seconds Returns: - Health status and API info if available + Health status dict """ import json as json_module import urllib.error @@ -862,6 +856,27 @@ class DockerMixin(MCPMixin): "error": str(e), } + @mcp_tool( + name="docker_health", + description="Check if a GhydraMCP container's API is responding", + ) + async def docker_health( + self, port: int = 8192, timeout: float = 5.0, ctx: Optional[Context] = None + ) -> Dict[str, Any]: + """Check if a GhydraMCP container's API is healthy. + + Args: + port: API port to check (default: 8192) + timeout: Request timeout in seconds + + Returns: + Health status and API info if available + """ + loop = asyncio.get_event_loop() + return await loop.run_in_executor( + None, self._sync_health_check, port, timeout + ) + @mcp_tool( name="docker_wait", description="Wait for a GhydraMCP container to become healthy", @@ -910,7 +925,7 @@ class DockerMixin(MCPMixin): async def docker_auto_start( self, binary_path: str, - wait: bool = True, + wait: bool = False, timeout: float = 300.0, ctx: Optional[Context] = None, ) -> Dict[str, Any]: @@ -927,7 +942,7 @@ class DockerMixin(MCPMixin): Args: binary_path: Path to the binary to analyze - wait: Wait for container to be ready (default: True) + wait: Wait for container to be ready (default: False, use docker_wait separately) timeout: Max wait time in seconds (default: 300) Returns: