{"record":{"id":"eec243f1c6b58b7c","repo":"different-ai/openwork","slug":"an-enterprise-mcp-server-url-must-use-http-or-http-eec243","errorCode":null,"errorMessage":"An enterprise MCP server URL must use HTTP or HTTPS.","messagePattern":"An enterprise MCP server URL must use HTTP or HTTPS\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/enterprise-mcp-client/src/requirements-discovery.ts","lineNumber":190,"sourceCode":"    input.authenticationRequired\n    && !input.authorizationServers.some((server) => server.clientIdMetadataDocumentSupported || server.registrationEndpoint)\n  ) {\n    requirements.unshift({\n      code: \"oauth_client_registration\",\n      label: \"Register an OAuth client\",\n      reason: \"The authorization server does not advertise client metadata documents or dynamic registration.\",\n      required: true,\n    })\n  }\n  return requirements\n}\n\nexport async function discoverConnectionRequirements(\n  input: DiscoverEnterpriseMcpConnectionRequirementsInput,\n): Promise<EnterpriseMcpConnectionRequirements> {\n  const serverUrl = new URL(input.serverUrl)\n  if (serverUrl.protocol !== \"http:\" && serverUrl.protocol !== \"https:\") {\n    throw new Error(\"An enterprise MCP server URL must use HTTP or HTTPS.\")\n  }\n  if (serverUrl.username || serverUrl.password || serverUrl.hash) {\n    throw new Error(\"An enterprise MCP server URL cannot contain credentials or a fragment.\")\n  }\n\n  const controller = new AbortController()\n  const timeout = setTimeout(() => controller.abort(new Error(\"MCP requirements discovery timed out.\")), input.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n  let lastStatus: number | undefined\n  let resourceMetadataUrl: URL | undefined\n  let challengeScope: string | undefined\n  const fetch = scopedFetch({\n    fetch: input.fetch,\n    signal: controller.signal,\n    observe: (response) => {\n      lastStatus = response.status\n      if (response.status !== 401 && response.status !== 403) return\n      const challenge = extractWWWAuthenticateParams(response)\n      resourceMetadataUrl = challenge.resourceMetadataUrl ?? resourceMetadataUrl","sourceCodeStart":172,"sourceCodeEnd":208,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/packages/enterprise-mcp-client/src/requirements-discovery.ts#L172-L208","documentation":"discoverConnectionRequirements validates the serverUrl with the URL constructor and rejects any protocol other than http: or https: before making any network request. Enterprise MCP servers are HTTP-based, so schemes like file:, ws:, or ftp: cannot be discovered. The message comes from a plain Error thrown at the very start of discovery.","triggerScenarios":"Passing a serverUrl whose parsed protocol is not http/https — e.g. \"mcp.example.com\" with no scheme (URL parsing fails or yields a file: style path is a different error; here explicitly ws://, ftp://, etc.), or a URL like \"localhost:8080\" which parses as protocol \"localhost:\".","commonSituations":"Omitting the scheme entirely so \"localhost:8080\" is parsed with protocol \"localhost:\"; hardcoding a ws:// URL meant for a raw WebSocket MCP transport; copying a non-HTTP endpoint from another tool's config.","solutions":["Prefix the URL with https:// (or http:// for local dev).","Fix config that stores scheme-less hosts: normalize by adding the scheme before calling discovery.","Use the streamable-HTTP MCP transport for enterprise servers, not raw ws:// URLs, in this discovery API."],"exampleFix":"// before\nserverUrl: \"mcp.internal.example.com/mcp\"\n// after\nserverUrl: \"https://mcp.internal.example.com/mcp\"","handlingStrategy":"validation","validationCode":"function assertHttpUrl(serverUrl: string): URL {\n  const u = new URL(serverUrl)\n  if (u.protocol !== \"http:\" && u.protocol !== \"https:\") {\n    throw new Error(`serverUrl must start with http:// or https://, got \"${u.protocol}\"`)\n  }\n  return u\n}","typeGuard":"function isHttpUrl(value: string): boolean {\n  try { const u = new URL(value); return u.protocol === \"http:\" || u.protocol === \"https:\" }\n  catch { return false }\n}","tryCatchPattern":"try { const reqs = await discoverConnectionRequirements({ serverUrl }) }\ncatch (e) {\n  if (e.message.includes(\"must use HTTP or HTTPS\")) {\n    throw new Error(`Invalid server URL \"${serverUrl}\": add an http:// or https:// scheme`)\n  }\n  throw e\n}","preventionTips":["Always store MCP server URLs with an explicit scheme in configuration","Validate URL scheme at config-load time with a Zod schema (z.url() + protocol refine)","Remember scheme-less \"host:port\" strings parse with a bogus protocol — always normalize first"],"tags":["url","validation","discovery","configuration","http"],"backgroundTag":"invalid-server-url-scheme","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}