fix(mcp): add --host flag to HTTP server bind address#232
Open
fix(mcp): add --host flag to HTTP server bind address#232
Conversation
--host flag to MCP HTTP server
added 2 commits
February 25, 2026 00:38
Reuse a single normalized host value for daemon output and EADDRINUSE messaging in qmd mcp --http path.
8738690 to
330452d
Compare
parseArgs with strict:false produces boolean true for bare --host and steals the next flag as the value for --host --daemon. Add a typeof guard in qmd.ts and strengthen validateMcpHostInput to reject flag-like values, host:port patterns (while preserving bare IPv6), and add corresponding unit and integration tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
|
Added follow-up hardening in |
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.
Summary
Re-implements the
--hostMCP HTTP bind fix on top of currentmain(v1.1.x code shape), while keeping default behavior unchanged.Closes #227.
Problem (still present on current
mainbefore this change)qmd mcp --httpbinds withlisten(port, "localhost").localhostresolves to::1, creating an IPv6-only listener.host.docker.internal(IPv4 path) cannot reach the daemon.Implementation (v1.1.x-compatible)
1) Add explicit host control
qmd mcp --http --host <ADDR> [--port N]--hostis forwarded in daemon mode as well.2) Centralize host normalization/validation
src/mcp-host.tswith:validateMcpHostInput(rawHost)normalizeMcpHost(rawHost)"[::1]" -> bind "::1", display "[::1]"localhost://or/) rejected with clear error3) Update MCP HTTP bind path
startMcpHttpServer(port, { quiet?, host? })bindHost) instead of hardcoded localhost.displayHost).4) Keep internal MCP Request URL host-independent
new Request(...)wrappers still usehttp://localhost:${listeningPort}/mcp.5) Fix latent ephemeral-port issue
listeningPortset inlisten()callback), avoiding:0on ephemeral binds.Files changed
src/mcp-host.ts(new)src/mcp.tssrc/qmd.tstest/mcp-host.test.ts(new)test/mcp.test.tstest/cli.test.tsREADME.mdCLAUDE.mdskills/qmd/references/mcp-setup.mdTests
Targeted
pnpm -s tsc -p tsconfig.build.jsonpnpm vitest run test/mcp-host.test.ts(5/5)pnpm vitest run test/mcp.test.ts(59/59)pnpm vitest run test/cli.test.ts(69/69)Full suite
pnpm vitest run test/(556/556, 13 files)Real-world pre-push smoke (Docker/Colima path)
Run in same host environment before push.
Case A: default bind (
qmd mcp --http)http://localhost:18921/health):200http://host.docker.internal:18921/health):000[::1]:18921Case B: explicit IPv4 (
qmd mcp --http --host 127.0.0.1)http://localhost:18922/health):200http://host.docker.internal:18922/health):200127.0.0.1:18922This reproduces #227 and confirms the fix.
Backward compatibility
--host, behavior remainslocalhostbind.Follow-up (
6ba8786):--hostvalidation hardeningFound
parseArgs({ strict: false })edge cases where malformed--hostvalues reached bind-time:--host→cli.values.host === true→ENOTFOUND true--host --daemon→cli.values.host === "--daemon"→ENOTFOUND --daemon--host localhost:8181→ENOTFOUND localhost:8181Fixes:
src/qmd.ts: reject non-string--hostvalues early.src/mcp-host.ts: reject flag-like values andhost:portinput; keep bare IPv6 valid vianet.isIP().Tests:
tsc -p tsconfig.build.json: cleanmcp-host.test.ts:9/9(was5)cli.test.ts:72/72(was69)563/563(local run; CI is the merge gate)