mcp-server-dns resolve_all and check_nameservers silently resolve the root zone on empty domain — agents get 13 nameservers instead of an error
Reproduced: inconsistent empty-domain handling in mcp-server-dns v1.0.1
Surface: mcp-server-dns v1.0.1 via npx -y mcp-server-dns (stdio)
The bug
When an agent passes an empty string as domain, three tools behave differently:
| Tool | domain: "" | Behavior |
|---|---|---|
dns_lookup | isError: true — queryA ENODATA | Correct — empty domain is rejected |
resolve_all | isError: undefined (success!) — returns 13 root servers | Wrong — silently resolves the DNS root zone |
check_nameservers | isError: undefined (success!) — returns 13 root servers | Wrong — silently resolves the DNS root zone |
The same inconsistency appears with domain: "." (explicit root zone):
dns_lookup(".")→isError: true, ENODATAresolve_all(".")→isError: undefined, returns root servers in NS section
Repro trace
resolve_all with empty domain — returns root servers as "success":
→ {"jsonrpc":"2.0","id":40,"method":"tools/call","params":{"name":"resolve_all","arguments":{"domain":""}}}
← {"result":{"content":[{"type":"text","text":"=== A ===\n(no records or error)\n\n=== AAAA ===\n(no records or error)\n\n=== MX ===\n(no records or error)\n\n=== TXT ===\n(no records or error)\n\n=== NS ===\nb.root-servers.net\nc.root-servers.net\nl.root-servers.net\nm.root-servers.net\nh.root-servers.net\nf.root-servers.net\ne.root-servers.net\nd.root-servers.net\n..."}]}}Note: isError is absent (success). The agent receives what looks like valid DNS data.
check_nameservers with empty domain — same root servers, same "success":
→ {"jsonrpc":"2.0","id":100,"method":"tools/call","params":{"name":"check_nameservers","arguments":{"domain":""}}}
← {"result":{"content":[{"type":"text","text":"b.root-servers.net\nc.root-servers.net\nl.root-servers.net\nm.root-servers.net\n...13 total..."}]}}dns_lookup with empty domain — correctly errors:
→ {"jsonrpc":"2.0","id":10,"method":"tools/call","params":{"name":"dns_lookup","arguments":{"domain":""}}}
← {"result":{"content":[{"type":"text","text":"queryA ENODATA"}],"isError":true}}Why this matters for agents
- Silent false positives: An agent checking "does domain X have nameservers?" with an empty/unset variable gets 13 root servers back and concludes "yes, it's properly configured" — a silent false positive.
- Data pollution: An agent building a domain profile with
resolve_allgets root zone NS data mixed into its results for what it believes is the target domain.
- Inconsistent validation: The same mistake (empty domain) either fails or succeeds depending on which tool you pick. This makes the API surface unpredictable — agents can't rely on consistent error behavior.
Root cause
The DNS root zone genuinely has NS records (the 13 root servers) but no A/AAAA records. Node.js dns.resolveNs('') returns the root's NS records while dns.resolve4('') returns ENODATA. resolve_all and check_nameservers surface the NS success path without pre-validating the domain, while dns_lookup happens to query A records first and hits ENODATA.
The fix is trivial: validate domain is non-empty before making any queries. All five tools should return isError: true for empty/blank domain input.
Additional notes from this test run
Good news — mcp-server-dns handles these edge cases correctly:
- Shell injection (
; ls /, backticks,$(cmd)) → EBADNAME - Newline injection → EBADNAME
- Null byte injection → caught
- Type validation (number instead of string) → proper Zod error
- Invalid record types → enum validation
- 300+ char domains → EBADNAME
- 64-char labels (exceeds DNS 63-char max) → EBADNAME
- IDN/unicode domains (münchen.de) → resolves correctly
- IPv6 reverse DNS → works correctly