{"record":{"id":"77c5f2e62b87f0c9","repo":"koala73/worldmonitor","slug":"serverurl-dns-resolution-returned-no-addresses","errorCode":null,"errorMessage":"serverUrl DNS resolution returned no addresses","messagePattern":"serverUrl DNS resolution returned no addresses","errorType":"exception","errorClass":"McpProxySsrfError","httpStatus":422,"severity":"error","filePath":"api/mcp-proxy.ts","lineNumber":172,"sourceCode":"async function assertServerUrlSafe(url) {\n  const hostname = url.hostname.toLowerCase();\n  if (BLOCKED_HOSTNAMES.has(hostname)) {\n    throw new McpProxySsrfError(`serverUrl hostname is blocked: ${hostname}`);\n  }\n  if (isBlockedResolvedAddress(hostname)) {\n    throwBlockedAddress(hostname);\n  }\n\n  let resolvedAddresses;\n  try {\n    resolvedAddresses = await defaultResolveHostname(hostname);\n  } catch (error) {\n    const message = error instanceof Error ? error.message : String(error);\n    throw new McpProxySsrfError(`serverUrl DNS resolution failed: ${message}`);\n  }\n\n  if (!resolvedAddresses.length) {\n    throw new McpProxySsrfError('serverUrl DNS resolution returned no addresses');\n  }\n\n  const blocked = resolvedAddresses.find(isBlockedResolvedAddress);\n  if (blocked) {\n    throwBlockedAddress(blocked);\n  }\n\n  return { url, resolvedAddresses };\n}\n\n// Vercel Edge fetch does not expose a Node-style lookup/socket hook, so this\n// proxy CANNOT pin the TLS connection to a previously vetted address. There is\n// no way to guarantee that the IP we validated is the IP fetch() ultimately\n// connects to; a DNS answer can change between our resolve and fetch's own\n// resolve. This re-resolve-and-recheck immediately before every outbound\n// dispatch NARROWS that DNS-rebinding window but does not close it. The\n// residual rebind window is an ACCEPTED limitation of the Edge runtime (no\n// socket-level pin available) — documented, not fixed here (P2, issue #5061).","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/koala73/worldmonitor/blob/ffec79ac339946fd2d24e85845da5755dcaa534b/api/mcp-proxy.ts#L154-L190","documentation":"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.","triggerScenarios":"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).","commonSituations":"Typo in the MCP server host; a domain not yet provisioned; an expired domain; a hostname that only has MX records.","solutions":["Verify the hostname spelling and registration.","Ensure the host has at least one A or AAAA record: `dig +short A <host>` / `dig +short AAAA <host>`.","Provision an A/AAAA record at the DNS provider and retry."],"exampleFix":"// before\nproxy({ serverUrl: 'https://mcp.typo.exmaple.com/mcp' })\n// after\nproxy({ serverUrl: 'https://mcp.example.com/mcp' })","handlingStrategy":"validation","validationCode":"async function hostHasAddressRecord(hostname: string): Promise<boolean> {\n  const r = await fetch(`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(hostname)}&type=A`, {\n    headers: { Accept: 'application/dns-json' },\n    signal: AbortSignal.timeout(2000),\n  });\n  const data = await r.json();\n  return Array.isArray(data?.Answer) && data.Answer.some(a => a?.type === 1);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await assertServerUrlSafe(new URL(serverUrl));\n} catch (err) {\n  if (err.name === 'McpProxySsrfError' && err.message === 'serverUrl DNS resolution returned no addresses') {\n    return res.status(400).json({ error: 'The MCP server host has no DNS address record.' });\n  }\n  throw err;\n}","preventionTips":["Verify the MCP server host has A/AAAA records before registering.","Make sure DNS is provisioned before first use.","Surface 'no addresses' as a user-correctable config error."],"tags":["mcp","ssrf","dns","proxy","validation"],"backgroundTag":null,"analyzedSha":"ffec79ac339946fd2d24e85845da5755dcaa534b","analyzedAt":"2026-08-12T11:24:56.012Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}