different-ai/openwork · error · LocalManagedMcpPrivateUrlError

managed MCP egress requires HTTPS

Error message

managed MCP egress requires HTTPS

What it means

Managed MCP server connections must use HTTPS so outbound traffic from the host is encrypted. assertLocalManagedMcpUrl rejects any http:// URL before a connection is created, throwing LocalManagedMcpPrivateUrlError with this message. It is a deliberate egress-security policy: the guard only allows plain HTTP when private URLs are explicitly permitted.

Source

Thrown at apps/server/src/local-managed-mcp-url-guard.ts:173

        return;
      }
      if (options.all) {
        callback(null, addresses);
        return;
      }
      const first = addresses[0];
      callback(null, first.address, first.family);
    }, (error: unknown) => {
      callback(error instanceof Error ? error : new Error("Managed MCP hostname lookup failed."), []);
    });
  };
}

export async function assertLocalManagedMcpUrl(rawUrl: string): Promise<void> {
  const url = parseHttpUrl(rawUrl);
  if (allowPrivateUrls()) return;
  if (url.protocol !== "https:") {
    throw new LocalManagedMcpPrivateUrlError(rawUrl, "managed MCP egress requires HTTPS");
  }
  const hostname = url.hostname.replace(/^\[|\]$/g, "");
  if (isIP(hostname)) {
    if (isLocalManagedMcpPrivateAddress(hostname)) {
      throw new LocalManagedMcpPrivateUrlError(rawUrl, "the address is private or reserved");
    }
    return;
  }
  let addresses: LookupAddress[];
  try {
    addresses = await resolveAddresses(hostname, { all: true, verbatim: true });
  } catch {
    throw new LocalManagedMcpPrivateUrlError(rawUrl, "the hostname does not resolve");
  }
  validateResolvedAddresses(hostname, addresses);
}

type FetchLike = (url: string | URL, init?: RequestInit) => Promise<Response>;

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Serve the MCP endpoint over HTTPS and change the URL scheme to https://
  2. Put a TLS-terminating proxy (e.g. ngrok, caddy) in front of a local HTTP server and use the https URL
  3. Enable the private-URL allowance for the environment (the allowPrivateUrls() switch) if this is intentionally a local/dev connection
  4. Verify the URL was not truncated or rewritten, losing an https scheme

Example fix

// before
await createLocalManagedMcpConnection({ url: "http://mcp.example.com/mcp" });
// after
await createLocalManagedMcpConnection({ url: "https://mcp.example.com/mcp" });
Defensive patterns

Strategy: validation

Validate before calling

const url = new URL(rawUrl);
if (url.protocol !== "https:") {
  throw new Error(`MCP URL must use https://, got: ${url.protocol}`);
}

Type guard

function isHttpsUrl(rawUrl: string): boolean {
  try {
    return new URL(rawUrl).protocol === "https:";
  } catch {
    return false;
  }
}

Try / catch

try {
  await createLocalManagedMcpConnection({ url });
} catch (error) {
  if (error instanceof LocalManagedMcpPrivateUrlError && error.message === "managed MCP egress requires HTTPS") {
    // surface a config-fix message: switch endpoint to https://
  }
  throw error;
}

Prevention

When it happens

Trigger: Calling createLocalManagedMcpConnection or withRemoteClient with a rawUrl whose parsed protocol is http: while allowPrivateUrls() is false (the default).

Common situations: Pointing a managed MCP connection at a local dev server (http://localhost:3000/mcp) or an internal test endpoint without TLS; configuring an MCP server URL copied from docs that default to http; missing the env/config flag that permits private/insecure URLs in dev.

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/a2adc6e7cbd1b3d0. Report an issue: GitHub.