{"record":{"id":"07de77763b016035","repo":"apache/shardingsphere","slug":"streamable-http-transport-url-must-be-an-http-url","errorCode":null,"errorMessage":"streamable-http transport URL must be an HTTP URL.","messagePattern":"streamable-http transport URL must be an HTTP URL\\.","errorType":"console","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"mcp/registry/src/main/java/org/apache/shardingsphere/mcp/registry/MCPRegistryMetadataCommand.java","lineNumber":249,"sourceCode":"    private static void validateVersion(final String label, final String value, final boolean allowSnapshot) {\n        ShardingSpherePreconditions.checkState(!value.isBlank() && !\"null\".equals(value), () -> new IllegalArgumentException(label + \" must be a non-empty string.\"));\n        ShardingSpherePreconditions.checkState(!\"latest\".equals(value), () -> new IllegalArgumentException(label + \" must not use latest.\"));\n        ShardingSpherePreconditions.checkState(!VERSION_RANGE_PATTERN.matcher(value).matches(),\n                () -> new IllegalArgumentException(label + \" must be a specific version, not a range.\"));\n        if (!allowSnapshot) {\n            ShardingSpherePreconditions.checkState(!value.contains(\"SNAPSHOT\"), () -> new IllegalArgumentException(label + \" must not contain SNAPSHOT for publication.\"));\n        }\n    }\n    \n    private static void validateHttpUrl(final Object value) {\n        ShardingSpherePreconditions.checkState(value instanceof String && !((String) value).isBlank(),\n                () -> new IllegalArgumentException(\"streamable-http transport must define a URL.\"));\n        try {\n            URI uri = new URI((String) value);\n            ShardingSpherePreconditions.checkState((\"http\".equals(uri.getScheme()) || \"https\".equals(uri.getScheme())) && null != uri.getHost(),\n                    () -> new IllegalArgumentException(\"streamable-http transport URL must be an HTTP URL.\"));\n        } catch (final URISyntaxException ex) {\n            throw new IllegalArgumentException(\"streamable-http transport URL must be an HTTP URL.\", ex);\n        }\n    }\n    \n    private static void validateEnvironmentVariable(final Map<String, Object> packageMetadata, final String name) {\n        Object envVars = packageMetadata.get(\"environmentVariables\");\n        ShardingSpherePreconditions.checkState(envVars instanceof List<?>, () -> new IllegalArgumentException(\"MCP Registry package must define \" + name + \".\"));\n        Map<?, ?> envVar = findEnvironmentVariable((List<?>) envVars, name);\n        ShardingSpherePreconditions.checkState(Boolean.FALSE.equals(envVar.get(\"isRequired\")),\n                () -> new IllegalArgumentException(String.format(\"MCP Registry metadata for %s must declare isRequired as false.\", name)));\n        ShardingSpherePreconditions.checkState(Boolean.FALSE.equals(envVar.get(\"isSecret\")),\n                () -> new IllegalArgumentException(String.format(\"MCP Registry metadata for %s must declare isSecret as false.\", name)));\n        ShardingSpherePreconditions.checkState(\"string\".equals(envVar.get(\"format\")),\n                () -> new IllegalArgumentException(String.format(\"MCP Registry metadata for %s format must be string.\", name)));\n    }\n    \n    private static Map<?, ?> findEnvironmentVariable(final List<?> envVars, final String name) {\n        return envVars.stream().filter(each -> each instanceof Map<?, ?>).map(each -> (Map<?, ?>) each).filter(each -> name.equals(each.get(\"name\"))).findFirst()\n                .orElseThrow(() -> new IllegalArgumentException(\"MCP Registry package must define \" + name + \".\"));","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/apache/shardingsphere/blob/e952770a215630a3659c75d64369168cd3e26b82/mcp/registry/src/main/java/org/apache/shardingsphere/mcp/registry/MCPRegistryMetadataCommand.java#L231-L267","documentation":"Thrown by MCPRegistryMetadataCommand.validateHttpUrl while validating registry metadata: the streamable-http transport entry has a URL, but it is not parseable as a URI with an http or https scheme and a non-null host. Non-HTTP schemes (file:, tcp:), missing host (bare 'localhost:8080'), and malformed URIs all fail.","triggerScenarios":"Publishing/validating MCP registry metadata where transports[streamable-http].url is e.g. \"localhost:8080/mcp\" (no scheme), \"tcp://host:port\", or contains characters that make new URI(...) throw URISyntaxException (spaces, unencoded brackets).","commonSituations":"Draft registry JSON hand-edited with a scheme-less URL, confusion between the streamable-http and stdio transports (stdio entries must not define a URL), or environment-specific URLs pasted without the https:// prefix.","solutions":["Set the transport URL to a full absolute HTTP(S) URL, e.g. https://host.example.com:8080/mcp, with a scheme and hostname.","URL-encode any special characters in the path or query so URI parsing succeeds.","Run the command with --validate-only before publishing to catch metadata errors early."],"exampleFix":"// before\n\"url\": \"localhost:8080/mcp\"\n// after\n\"url\": \"https://localhost:8080/mcp\"","handlingStrategy":"validation","validationCode":"try {\n    URI uri = URI.create((String) transports.get(\"streamable-http\").get(\"url\"));\n    if (!(\"http\".equals(uri.getScheme()) || \"https\".equals(uri.getScheme())) || uri.getHost() == null) throw new IllegalArgumentException(\"bad url\");\n} catch (final IllegalArgumentException ex) { /* fix metadata before publishing */ }","typeGuard":"const isHttpUrl = u => { try { const x = new URL(u); return x.protocol === \"http:\" || x.protocol === \"https:\"; } catch { return false; } };","tryCatchPattern":"try {\n    command.run(args); // include --validate-only\n} catch (final IllegalArgumentException ex) {\n    // fix the transport url in the registry JSON; rerun with --validate-only until clean\n}","preventionTips":["Always include scheme and host in transport URLs (https://host/path)","Run --validate-only in CI on every metadata change","Leave the url field absent for stdio transports"],"tags":["registry","metadata","url-validation","http"],"backgroundTag":null,"analyzedSha":"e952770a215630a3659c75d64369168cd3e26b82","analyzedAt":"2026-08-14T13:54:53.392Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}