{"record":{"id":"a2adc6e7cbd1b3d0","repo":"different-ai/openwork","slug":"managed-mcp-egress-requires-https","errorCode":null,"errorMessage":"managed MCP egress requires HTTPS","messagePattern":"managed MCP egress requires HTTPS","errorType":"exception","errorClass":"LocalManagedMcpPrivateUrlError","httpStatus":null,"severity":"error","filePath":"apps/server/src/local-managed-mcp-url-guard.ts","lineNumber":173,"sourceCode":"        return;\n      }\n      if (options.all) {\n        callback(null, addresses);\n        return;\n      }\n      const first = addresses[0];\n      callback(null, first.address, first.family);\n    }, (error: unknown) => {\n      callback(error instanceof Error ? error : new Error(\"Managed MCP hostname lookup failed.\"), []);\n    });\n  };\n}\n\nexport async function assertLocalManagedMcpUrl(rawUrl: string): Promise<void> {\n  const url = parseHttpUrl(rawUrl);\n  if (allowPrivateUrls()) return;\n  if (url.protocol !== \"https:\") {\n    throw new LocalManagedMcpPrivateUrlError(rawUrl, \"managed MCP egress requires HTTPS\");\n  }\n  const hostname = url.hostname.replace(/^\\[|\\]$/g, \"\");\n  if (isIP(hostname)) {\n    if (isLocalManagedMcpPrivateAddress(hostname)) {\n      throw new LocalManagedMcpPrivateUrlError(rawUrl, \"the address is private or reserved\");\n    }\n    return;\n  }\n  let addresses: LookupAddress[];\n  try {\n    addresses = await resolveAddresses(hostname, { all: true, verbatim: true });\n  } catch {\n    throw new LocalManagedMcpPrivateUrlError(rawUrl, \"the hostname does not resolve\");\n  }\n  validateResolvedAddresses(hostname, addresses);\n}\n\ntype FetchLike = (url: string | URL, init?: RequestInit) => Promise<Response>;","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/server/src/local-managed-mcp-url-guard.ts#L155-L191","documentation":"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.","triggerScenarios":"Calling createLocalManagedMcpConnection or withRemoteClient with a rawUrl whose parsed protocol is http: while allowPrivateUrls() is false (the default).","commonSituations":"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.","solutions":["Serve the MCP endpoint over HTTPS and change the URL scheme to https://","Put a TLS-terminating proxy (e.g. ngrok, caddy) in front of a local HTTP server and use the https URL","Enable the private-URL allowance for the environment (the allowPrivateUrls() switch) if this is intentionally a local/dev connection","Verify the URL was not truncated or rewritten, losing an https scheme"],"exampleFix":"// before\nawait createLocalManagedMcpConnection({ url: \"http://mcp.example.com/mcp\" });\n// after\nawait createLocalManagedMcpConnection({ url: \"https://mcp.example.com/mcp\" });","handlingStrategy":"validation","validationCode":"const url = new URL(rawUrl);\nif (url.protocol !== \"https:\") {\n  throw new Error(`MCP URL must use https://, got: ${url.protocol}`);\n}","typeGuard":"function isHttpsUrl(rawUrl: string): boolean {\n  try {\n    return new URL(rawUrl).protocol === \"https:\";\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"try {\n  await createLocalManagedMcpConnection({ url });\n} catch (error) {\n  if (error instanceof LocalManagedMcpPrivateUrlError && error.message === \"managed MCP egress requires HTTPS\") {\n    // surface a config-fix message: switch endpoint to https://\n  }\n  throw error;\n}","preventionTips":["Always configure MCP endpoints with explicit https:// schemes","Put local dev servers behind a TLS proxy (ngrok, caddy) instead of using http://","Add a startup config validation that rejects non-https MCP URLs early","Keep dev-only insecure-URL allowances limited to local environments"],"tags":["security","network","https","url-validation"],"backgroundTag":"insecure-egress-url-rejected","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}