different-ai/openwork · error · LocalManagedMcpPrivateUrlError
the hostname resolves to a private or reserved address (${ad
Error message
the hostname resolves to a private or reserved address (${address}) What it means
LocalManagedMcpPrivateUrlError thrown by validateResolvedAddresses when any resolved IP for the MCP hostname is a private/reserved address (loopback, RFC1918, link-local, etc.) while private URLs are not allowed. This is an SSRF protection: remote-looking hostnames that actually resolve to internal networks are rejected. Dev can bypass via OPENWORK_DEV_MODE=1 or OPENWORK_ALLOW_PRIVATE_MCP_URLS=1.
Source
Thrown at apps/server/src/local-managed-mcp-url-guard.ts:132
return url;
}
function allowPrivateUrls(): boolean {
return process.env.OPENWORK_DEV_MODE === "1" || process.env.OPENWORK_ALLOW_PRIVATE_MCP_URLS === "1";
}
type ResolveAddresses = (hostname: string, options: LookupAllOptions) => Promise<LookupAddress[]>;
const resolveAddresses: ResolveAddresses = (hostname, options) => lookup(hostname, options);
function validateResolvedAddresses(hostname: string, addresses: LookupAddress[]): void {
if (addresses.length === 0) {
throw new LocalManagedMcpPrivateUrlError(`https://${hostname}/`, "the hostname does not resolve");
}
if (allowPrivateUrls()) return;
for (const { address } of addresses) {
if (isLocalManagedMcpPrivateAddress(address)) {
throw new LocalManagedMcpPrivateUrlError(
`https://${hostname}/`,
`the hostname resolves to a private or reserved address (${address})`,
);
}
}
}
/**
* Resolves and validates the address inside the socket connector's lookup
* callback. The exact validated answer is handed to net.connect, so a later
* DNS answer cannot replace it between validation and connection.
*/
export function createLocalManagedMcpPublicLookup(
resolver: ResolveAddresses = resolveAddresses,
): LookupFunction {
return (hostname, options, callback) => {
const lookupOptions: LookupAllOptions = { ...options, all: true, verbatim: true };
void resolver(hostname, lookupOptions).then((addresses) => {View on GitHub (pinned to 2b7df46e8a)
Solutions
- If the private URL is intentional (local dev), set OPENWORK_ALLOW_PRIVATE_MCP_URLS=1 (or OPENWORK_DEV_MODE=1) for the server process
- Otherwise point the config at the server's real public hostname/IP
- Check /etc/hosts and DNS for entries mapping the hostname to a private address
- Never disable the guard in production-facing deployments — it exists to block SSRF
Example fix
// before
{ "url": "https://my-mcp.example.com/sse" } // resolves to 127.0.0.1
// after (local dev, intentional)
OPENWORK_ALLOW_PRIVATE_MCP_URLS=1 openwork-server ...
// or point at the public address
{ "url": "https://mcp-prod.example.com/sse" } Defensive patterns
Strategy: validation
Validate before calling
import { lookup } from "node:dns/promises";
function isPrivate(ip: string): boolean {
return ip === "::1" || ip.startsWith("127.") || ip.startsWith("10.") || ip.startsWith("192.168.") || /^172\.(1[6-9]|2\d|3[01])\./.test(ip) || ip.startsWith("169.254.");
}
const addrs = await lookup(new URL(url).hostname, { all: true });
if (addrs.some(a => isPrivate(a.address)) && process.env.OPENWORK_ALLOW_PRIVATE_MCP_URLS !== "1") throw new Error("hostname resolves to a private address; set OPENWORK_ALLOW_PRIVATE_MCP_URLS=1 if intentional"); Try / catch
try {
await mcp.addServer({ url });
} catch (e) {
if (e instanceof LocalManagedMcpPrivateUrlError && e.message.includes("private or reserved")) {
if (isLocalDev) process.env.OPENWORK_ALLOW_PRIVATE_MCP_URLS = "1"; // then retry
else throw new Error("refusing internal-resolving host in production");
} else throw e;
} Prevention
- Set OPENWORK_ALLOW_PRIVATE_MCP_URLS=1 only in local/dev environments, never in prod
- Check /etc/hosts and DNS for private-IP mappings of the hostname
- Treat this error as an SSRF alarm — investigate before bypassing
When it happens
Trigger: lookup(hostname) returns at least one address for which isLocalManagedMcpPrivateAddress(address) is true, and allowPrivateUrls() is false (neither env flag set to "1"). E.g. a public hostname with a DNS rebinding/hosts-file entry pointing at 127.0.0.1 or 10.x.x.x.
Common situations: Intentionally running an MCP server on localhost/LAN while the guard expects public URLs — needs the dev-mode env flag; /etc/hosts entries redirecting a name to 127.0.0.1; DNS rebinding protection firing on a legitimately internal service.
Related errors
- MCP_URL_BLOCKED
- Attachment hostname ${hostname} resolved to a private or res
- embedded URL credentials are not allowed
- the hostname does not resolve
- the address is private or reserved
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/a2619e80ff63e86c.
Report an issue: GitHub.