koala73/worldmonitor · error · McpProxySsrfError

serverUrl DNS resolution returned no addresses

Error message

serverUrl DNS resolution returned no addresses

What it means

Thrown by assertServerUrlSafe when defaultResolveHostname resolved without throwing but returned an empty array — the hostname has no A or AAAA records (NXDOMAIN-style). The proxy refuses to forward to a host with no resolvable address because there is nothing to SSRF-check or connect to.

Source

Thrown at api/mcp-proxy.ts:172

async function assertServerUrlSafe(url) {
  const hostname = url.hostname.toLowerCase();
  if (BLOCKED_HOSTNAMES.has(hostname)) {
    throw new McpProxySsrfError(`serverUrl hostname is blocked: ${hostname}`);
  }
  if (isBlockedResolvedAddress(hostname)) {
    throwBlockedAddress(hostname);
  }

  let resolvedAddresses;
  try {
    resolvedAddresses = await defaultResolveHostname(hostname);
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    throw new McpProxySsrfError(`serverUrl DNS resolution failed: ${message}`);
  }

  if (!resolvedAddresses.length) {
    throw new McpProxySsrfError('serverUrl DNS resolution returned no addresses');
  }

  const blocked = resolvedAddresses.find(isBlockedResolvedAddress);
  if (blocked) {
    throwBlockedAddress(blocked);
  }

  return { url, resolvedAddresses };
}

// Vercel Edge fetch does not expose a Node-style lookup/socket hook, so this
// proxy CANNOT pin the TLS connection to a previously vetted address. There is
// no way to guarantee that the IP we validated is the IP fetch() ultimately
// connects to; a DNS answer can change between our resolve and fetch's own
// resolve. This re-resolve-and-recheck immediately before every outbound
// dispatch NARROWS that DNS-rebinding window but does not close it. The
// residual rebind window is an ACCEPTED limitation of the Edge runtime (no
// socket-level pin available) — documented, not fixed here (P2, issue #5061).

View on GitHub (pinned to ffec79ac33)

Solutions

  1. Verify the hostname spelling and registration.
  2. Ensure the host has at least one A or AAAA record: `dig +short A <host>` / `dig +short AAAA <host>`.
  3. Provision an A/AAAA record at the DNS provider and retry.

Example fix

// before
proxy({ serverUrl: 'https://mcp.typo.exmaple.com/mcp' })
// after
proxy({ serverUrl: 'https://mcp.example.com/mcp' })
Defensive patterns

Strategy: validation

Validate before calling

async function hostHasAddressRecord(hostname: string): Promise<boolean> {
  const r = await fetch(`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(hostname)}&type=A`, {
    headers: { Accept: 'application/dns-json' },
    signal: AbortSignal.timeout(2000),
  });
  const data = await r.json();
  return Array.isArray(data?.Answer) && data.Answer.some(a => a?.type === 1);
}

Try / catch

try {
  await assertServerUrlSafe(new URL(serverUrl));
} catch (err) {
  if (err.name === 'McpProxySsrfError' && err.message === 'serverUrl DNS resolution returned no addresses') {
    return res.status(400).json({ error: 'The MCP server host has no DNS address record.' });
  }
  throw err;
}

Prevention

When it happens

Trigger: POST /api/mcp-proxy with a serverUrl whose hostname does not exist, has been de-registered, or has only non-address records (MX/TXT only).

Common situations: Typo in the MCP server host; a domain not yet provisioned; an expired domain; a hostname that only has MX records.

Understand the failure class

Related errors


AI-assisted analysis of koala73/worldmonitor@ffec79ac33 (2026-08-12). Data as JSON: /api/errors/77c5f2e62b87f0c9. Report an issue: GitHub.