Open
Conversation
Allow users to specify the bind address for the MCP HTTP server, solving IPv6/IPv4 mismatch when Docker containers (Colima) route host.docker.internal to 127.0.0.1 while Node resolves localhost to ::1. - Add host option to startMcpHttpServer() with IPv6-safe URL formatting - Add --host to CLI parseArgs, daemon spawn forwarding, and help text - Validate --host input (reject URLs) and normalize bracketed IPv6 - Set listeningPort inside listen() callback for correct --port 0 handling - Update forwarding URLs to use configured host instead of hardcoded localhost - Add 7 tests: default host, explicit IPv4, AddressInfo, MCP handshake, bracketed IPv6 normalization, daemon forwarding, invalid input rejection - Update CLAUDE.md, README.md, mcp-setup.md with --host examples Closes tobi#227 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
feat(mcp): add --host flag to HTTP server bind address
Problem
On macOS, Node.js resolves
localhostto::1(IPv6 loopback) via the system resolver. Docker containers using Colima or similar runtimes routehost.docker.internalto IPv4127.0.0.1. This mismatch causes silent connection failures when agents or downstream tools (e.g., OpenClaw) try to reach the QMD daemon from inside a container.There was no way to explicitly control the bind address — QMD always bound to
localhost, making the failure unrecoverable without external workarounds.Solution
Add a
--hostflag toqmd mcp --httpthat lets users specify the bind address explicitly.Changes
src/mcp.ts
startMcpHttpServer()accepts optionalhostparameter[::1]→::1) for socket bind::1→[::1]) per HTTP speclisteningPortassignment insidelisten()callback — ensures internal forwarding URLs use the resolved port when using--port 0localhostsrc/qmd.ts
hosttoparseArgsoptions--hostinput: rejects values containing://or/(catches--host http://127.0.0.1)--hostto daemon child process viaspawnArgsDocumentation
CLAUDE.md: Updated CLI referenceREADME.md: Added--hostexample with Docker/Colima contextskills/qmd/references/mcp-setup.md: Updated HTTP mode examplesTests (test/mcp.test.ts)
Seven new tests in two groups:
Host binding (5 tests):
AddressInfo[::1]→::1at socket level)Daemon --host forwarding (2 tests):
--hostto child process (verified via health endpoint polling)--hostvalue (URL) exits with code 1 and descriptive errorAll tests use isolated state directories (
XDG_CACHE_HOME,QMD_CONFIG_DIR) to prevent interference with real user daemons.Test plan
tsc— no type errorsvitest test/mcp.test.ts— 63/63 pass (7 new)vitest test/cli.test.ts— 66/66 pass (existing daemon tests unaffected)vitest test/— 495 passed tests, 0 failed tests; 11 passed files, 1 failed file (structured-search.test.tsimport error, pre-existing onmaintoo)--host 127.0.0.1, foreground default, daemon--host 127.0.0.1, help textBackward Compatibility
Fully backward compatible. Without
--host, behavior is identical to before (binds tolocalhost). No changes to stdio MCP transport, CLI commands, or search functionality.Usage with Docker / OpenClaw
Follow-up Items
These are minor refinements identified during review. None block this merge.
formatHostForUrl()helper — Bracket-stripping and re-bracketing logic appears in 3 sites (mcp.ts,qmd.tsCLI normalization,qmd.tsdisplay formatting). Extract a shared helper to single-source the normalization contract.net.isIP()— For inputs that look like numeric IPs, usenet.isIP()to catch structurally invalid addresses like192.168.1.999before they fail with cryptic EADDRNOTAVAIL at bind time. Must not reject valid hostnames (e.g.,host.docker.internal) — only validate when the input is clearly intended as a numeric address.19876 + random) instead of--port 0because the daemon spawns as a detached process and the test needs a known port to poll. Add an inline comment explaining this constraint for future maintainers.Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com
Co-Authored-By: ChatGPT 5.3-Codex noreply@openai.com
Closes #227