Hmbown/CodeWhale · error · ConversionError
Invalid MCP endpoint URL.
Error message
Invalid MCP endpoint URL.
What it means
remote_server() validates MCP server endpoint URLs and raises this ConversionError when Python's urlsplit itself fails to parse the URL — malformed syntax that urlsplit cannot turn into a structured result.
Solutions
- Fix the URL so urlsplit parses it: bracket IPv6 hosts, use a numeric port
- Remove template placeholders and interpolate real values before conversion
- Prefer a canonical https://host:port form and re-run the converter
Example fix
// before url: "http://[::1:notaport" // after url: "http://[::1]:8080"
Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlsplit
p = urlsplit(url)
assert p.scheme in ("http", "https") and p.hostname and p.port is not None Type guard
def looks_like_valid_url(url: str) -> bool:
try:
return bool(urlsplit(url).hostname)
except ValueError:
return False Try / catch
try:
validate_mcp_url(url)
except ValueError as e:
log.error("malformed MCP URL %r: %s", url, e) Prevention
- Interpolate templates before validation
- Bracket IPv6 literals
- Always include an explicit numeric port
When it happens
Trigger: A URL that makes parsed.port or hostname access raise ValueError: unparseable port (e.g. 'https://host:notaport'), malformed IPv6 brackets, or otherwise invalid URL syntax.
Common situations: Typos in the port field; unbracketed IPv6 literals; template placeholders left in config like {{PORT}}; URLs copied with trailing garbage.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
- Use a canonical numeric MCP address.
- Invalid MCP URL
- MCP HTTP requires an http:// or https:// URL with a host
- MCP HTTP URL must not contain credentials; use configured…
- MCP server URL ' ' must include a host
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/5e81466577e9ae59.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/convert-plugin.py:229
def remote_server(config, dialect, defaults):
mapping(config)
if dialect == "dsh":
require(config.get("transport") == "streamable-http", "Unsupported MCP transport.")
mapping(config, {"serverName", "transport", "url", "headers", "toolCallTimeoutMs", "failOnStartupError"})
else:
require(config.get("type") == "remote", "Unsupported MCP transport.")
mapping(config, {"type", "url", "headers", "oauth", "enabled" if dialect == "opencode-v1" else "disabled", "timeout"})
require(config.get("oauth") is False, "Set oauth:false explicitly; plugin OAuth and upstream auto-OAuth cannot be converted.")
extension = server_options(config, dialect, defaults)
url = config.get("url")
require(isinstance(url, str) and not re.search(r"[\s\\{}]", url), "MCP URL must be a literal endpoint without interpolation.")
try:
parsed = urlsplit(url)
host = parsed.hostname
require(bool(host) and parsed.port != 0, "MCP URL needs a valid host and port.")
except ValueError:
raise ConversionError("Invalid MCP endpoint URL.") from None
require(parsed.scheme == "https" or (parsed.scheme == "http" and host in {"localhost", "127.0.0.1", "::1"}),
"MCP endpoints need HTTPS (or explicit loopback HTTP).")
require(parsed.username is None and parsed.password is None and not parsed.query and not parsed.fragment,
"MCP URLs must not contain credentials, query strings or fragments.")
require(host.isascii() and len(url) <= 4096, "Use an ASCII MCP hostname and URL of at most 4096 characters.")
# URL implementations disagree on shorthand/hex IPv4 spellings. Emit only
# canonical numeric hosts so the reviewed native host set is identical.
if ":" in host or re.fullmatch(r"(?:[0-9]+|0x[0-9a-f]+)", host.rsplit(".", 1)[-1]):
try:
address = ipaddress.ip_address(host)
require(str(address) == host, "Use a canonical numeric MCP address.")
host = f"[{host}]" if address.version == 6 else host
except ValueError:
raise ConversionError("Use a canonical numeric MCP address.") from None
headers = mapping(config.get("headers", {}))
require(len(headers) <= 64, "At most 64 environment-backed headers are supported.")
env_headers = {}
seen_headers = set()View on GitHub (pinned to 433685b202)