docker_wait was the same anti-pattern as wait param - it blocked
a single tool call for up to 5 minutes with no visibility.
LLMs should poll docker_health(port) in their own loop. This gives:
- Visibility into progress between polls
- Ability to check docker_logs while waiting
- Control over timeout and retry logic
- Opportunity to bail out early
The wait parameter was a convenience anti-pattern that caused LLMs
to block on a single tool call for up to 5 minutes with no visibility
into progress.
Now docker_auto_start always returns immediately. Clients should use
docker_wait(port) separately to poll for container readiness. This
gives visibility into progress and allows early bailout.
Previously only docker_health was fixed to use run_in_executor(),
but all other Docker operations (docker_status, docker_start,
docker_stop, docker_logs, docker_build, docker_cleanup) still
used synchronous subprocess.run() which blocked the async event
loop. This caused docker_auto_start(wait=True) to freeze the
entire MCP server.
Now _run_docker_cmd is async and runs subprocess calls in thread
executor. All callers updated to use await.
- 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)
- docker_stop now validates container belongs to current session
before stopping (prevents one agent from stopping another's work)
- docker_cleanup now defaults to session_only=True for safety
(agents can still use session_only=False with caution)
Addresses audit finding: tools could cause cross-session interference
Ports are now always allocated from the pool (8192-8199) automatically.
This prevents session collisions where different agents would specify
the same port and interfere with each other.
Clients can't accidentally (or intentionally) override the port allocation
— the pool manager handles all assignments.
Refactors Docker mixin to support multiple Claude processes sharing
the same MCP server without port/container conflicts:
- PortPool class with flock-based cross-process synchronization
- Session-scoped container naming with UUID prefixes
- Docker label-based tracking for cross-process container discovery
- Automatic port allocation from pool (8192-8199)
- Cleanup mechanism for orphaned containers and stale locks
- New tools: docker_cleanup, docker_session_info
- Add ghydramcp Python package with FastMCP server implementation
- Add docker-compose.yml for easy container management
- Add Makefile with build/run targets
- Add QUICKSTART.md for getting started
- Add uv.lock for reproducible dependencies