diff --git a/README.md b/README.md index 4bf4337..06eaa18 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,42 @@ claude mcp add --transport http comfy http://127.0.0.1:9188/mcp Then in Claude Code, `/mcp` should list the five tools. +## Running ComfyUI in Docker / on another host + +The bridge binds `127.0.0.1` by default, reachable only from the same host (inside +the container). To reach it from an agent on another machine: + +1. **Bind all interfaces** — set `COMFY_BRIDGE_MCP_HOST=0.0.0.0` in the container. +2. **Publish the port** — `-p 9188:9188` (or `ports: ["9188:9188"]` in compose). +3. **Point the agent at the server IP**, not localhost: + ```bash + claude mcp add --transport http comfy http://192.168.1.12:9188/mcp + codex mcp add comfy --url http://192.168.1.12:9188/mcp + ``` + For the Codex *plugin*, edit `.codex-plugin/mcp.json`'s `url` to the server IP + (or just use `codex mcp add` above). + +`COMFY_BASE_URL` stays `http://127.0.0.1:8188` — the bridge runs *inside* the +ComfyUI container and reaches ComfyUI over the container's own localhost. + +**Images across hosts.** Images cross **by file path**, so the path `comfy_pull` +returns must point at the same file the agent can open. With a **shared folder** +between the container and your machine: + +- Put the temp dir in the share, e.g. `COMFY_BRIDGE_TMP=/shared/comfy_tmp` (in the container). +- Mount that share at the **same absolute path** on your machine, so + `/shared/comfy_tmp/img_*.png` resolves on both sides — then `comfy_pull` / + `comfy_push` image paths work unchanged. +- If the mount paths **differ** between container and host, the raw path won't + resolve agent-side. Either make the paths identical, or switch to base64-inline + images (not yet implemented — open an issue if you need it). + +Text needs none of this — it's inline. + +> **Security:** binding `0.0.0.0` exposes the bridge — including `comfy_run_workflow` +> (runs saved workflows) and file-path reads — to your LAN with no authentication. +> Keep it on a trusted network or firewall the port. + ## MCP tools | Tool | Direction | Purpose | @@ -165,6 +201,7 @@ prompt and an `Agent Emit(channel=render)` on the output, then from the agent: | Variable | Default | Effect | | --- | --- | --- | +| `COMFY_BRIDGE_MCP_HOST` | `127.0.0.1` | Interface the MCP bridge binds. Set `0.0.0.0` to reach it from another host (e.g. ComfyUI in Docker). See [Running ComfyUI in Docker](#running-comfyui-in-docker--on-another-host). | | `COMFY_BRIDGE_MCP_PORT` | `9188` | Port the MCP bridge binds (use `0` for an ephemeral port, e.g. in tests). | | `COMFY_BRIDGE_TMP` | `.comfy_bridge_tmp` | Temp dir where `Agent Emit` writes image PNGs. | | `COMFY_BRIDGE_TMP_TTL` | `3600` | Age (seconds) after which `save_tensor_png` reaps old `img_*.png` temp files. | diff --git a/src/bridge/mcp_server.py b/src/bridge/mcp_server.py index 46d65cc..87d3922 100644 --- a/src/bridge/mcp_server.py +++ b/src/bridge/mcp_server.py @@ -28,15 +28,28 @@ def registered_tool_names(mcp: FastMCP) -> list: return [t.name for t in getattr(mcp, "list_tools", lambda: [])()] -def start_in_background(host: str = "127.0.0.1", port: int = None, +def _resolve_bind(host, port): + """Resolve (host, port) from explicit args, else env, else defaults. + + Set COMFY_BRIDGE_MCP_HOST=0.0.0.0 to reach the bridge from outside the host + (e.g. ComfyUI in Docker; also publish the port, and point the agent at the + server's LAN IP instead of 127.0.0.1). + """ + if host is None: + host = os.environ.get("COMFY_BRIDGE_MCP_HOST", "127.0.0.1") + if port is None: + port = int(os.environ.get("COMFY_BRIDGE_MCP_PORT", "9188")) + return host, port + + +def start_in_background(host: str = None, port: int = None, _test_no_serve: bool = False) -> None: global _started with _lock: if _started: return _started = True - port = port if port is not None else int( - os.environ.get("COMFY_BRIDGE_MCP_PORT", "9188")) + host, port = _resolve_bind(host, port) mcp = build_server(host=host, port=port) if _test_no_serve: return diff --git a/tests/bridge/test_mcp_bind.py b/tests/bridge/test_mcp_bind.py new file mode 100644 index 0000000..29e1a17 --- /dev/null +++ b/tests/bridge/test_mcp_bind.py @@ -0,0 +1,19 @@ +from src.bridge import mcp_server + + +def test_resolve_bind_defaults(monkeypatch): + monkeypatch.delenv("COMFY_BRIDGE_MCP_HOST", raising=False) + monkeypatch.delenv("COMFY_BRIDGE_MCP_PORT", raising=False) + assert mcp_server._resolve_bind(None, None) == ("127.0.0.1", 9188) + + +def test_resolve_bind_reads_env(monkeypatch): + monkeypatch.setenv("COMFY_BRIDGE_MCP_HOST", "0.0.0.0") + monkeypatch.setenv("COMFY_BRIDGE_MCP_PORT", "9999") + assert mcp_server._resolve_bind(None, None) == ("0.0.0.0", 9999) + + +def test_resolve_bind_explicit_args_override_env(monkeypatch): + monkeypatch.setenv("COMFY_BRIDGE_MCP_HOST", "0.0.0.0") + monkeypatch.setenv("COMFY_BRIDGE_MCP_PORT", "9999") + assert mcp_server._resolve_bind("127.0.0.1", 1234) == ("127.0.0.1", 1234)