{"record":{"id":"9f190debeda692f4","repo":"unslothai/unsloth","slug":"url-is-missing-a-host","errorCode":null,"errorMessage":"url is missing a host","messagePattern":"url is missing a host","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"studio/backend/routes/mcp_servers.py","lineNumber":105,"sourceCode":"                \"first token is an executable (not a URL).\",\n            )\n        return trimmed\n    parsed = urlparse(trimmed)\n    if parsed.scheme not in (\"http\", \"https\"):\n        if _looks_like_command(trimmed):\n            detail = (\n                \"Local commands aren't enabled on this server. To allow them, \"\n                \"set UNSLOTH_STUDIO_ALLOW_STDIO_MCP=1 and restart Unsloth, or use \"\n                \"an http:// or https:// URL instead.\"\n            )\n        else:\n            detail = (\n                \"MCP server address must start with http:// or https:// \"\n                \"(for example https://example.com/mcp).\"\n            )\n        raise HTTPException(status_code = 400, detail = detail)\n    if not parsed.netloc:\n        raise HTTPException(status_code = 400, detail = \"url is missing a host\")\n    return trimmed\n\n\ndef _normalize_headers(headers: dict[str, str] | None) -> dict[str, str] | None:\n    \"\"\"Trim header names, drop empties, coerce values to str; None if empty.\"\"\"\n    if not headers:\n        return None\n    out: dict[str, str] = {}\n    for raw_key, value in headers.items():\n        key = str(raw_key).strip()\n        if key:\n            out[key] = str(value)\n    return out or None\n\n\ndef _row_to_response(row: dict) -> McpServerResponse:\n    return McpServerResponse(\n        id = row[\"id\"],","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/mcp_servers.py#L87-L123","documentation":"400 raised after scheme validation passes (http:// or https://) but urlparse finds no netloc — the authority/host component is empty. This catches malformed URLs where the host slot is blank, since a URL without a host cannot be dialed.","triggerScenarios":"POST/PUT with url values like 'http://', 'http:///path', 'https://:8080', or 'http:// /x' where everything lands in the path and the netloc stays empty.","commonSituations":"String-concatenation bugs building URLs from empty host variables (f'http://{host}/mcp' with host=''), or typos adding extra slashes after the scheme.","solutions":["Fix the URL to include a host: 'https://example.com/mcp', 'http://127.0.0.1:8080/mcp'.","When templating, assert the host variable is non-empty before building the URL.","Watch for doubled slashes after the scheme that push the host into the path."],"exampleFix":"# before\nurl = f'http://{host}/mcp'   # host == '' -> 'http:///mcp' -> 400\n\n# after\nassert host, 'MCP host must be set'\nurl = f'http://{host}/mcp'","handlingStrategy":"validation","validationCode":"try { const u = new URL(url); if (!u.hostname) throw new Error('missing host'); } catch { throw new Error('invalid MCP url'); }","typeGuard":"function urlHasHost(v) { try { return new URL(v).hostname.length > 0; } catch { return false; } }","tryCatchPattern":null,"preventionTips":["Template URLs only after asserting the host variable is non-empty.","Avoid extra slashes between scheme and host."],"tags":["mcp","url","validation","http-400"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}