{"record":{"id":"0e5136fe1a498d58","repo":"sipeed/picoclaw","slug":"invalid-mcp-url-q","errorCode":null,"errorMessage":"invalid MCP URL %q","messagePattern":"invalid MCP URL %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/picoclaw/internal/mcp/add.go","lineNumber":214,"sourceCode":"\t\tEnabled:  true,\n\t\tType:     transport,\n\t\tDeferred: opts.Deferred,\n\t}\n\n\tswitch transport {\n\tcase \"http\", \"sse\":\n\t\tif len(env) > 0 {\n\t\t\treturn config.MCPServerConfig{}, fmt.Errorf(\"--env can only be used with stdio transport\")\n\t\t}\n\t\tif strings.TrimSpace(opts.EnvFile) != \"\" {\n\t\t\treturn config.MCPServerConfig{}, fmt.Errorf(\"--env-file can only be used with stdio transport\")\n\t\t}\n\t\tif len(args) > 0 {\n\t\t\treturn config.MCPServerConfig{}, fmt.Errorf(\"%s transport does not accept command arguments\", transport)\n\t\t}\n\t\tparsedURL, err := url.ParseRequestURI(target)\n\t\tif err != nil || parsedURL.Scheme == \"\" || parsedURL.Host == \"\" {\n\t\t\treturn config.MCPServerConfig{}, fmt.Errorf(\"invalid MCP URL %q\", target)\n\t\t}\n\t\tserver.URL = target\n\t\tserver.Headers = headers\n\t\treturn server, nil\n\t}\n\n\tif len(headers) > 0 {\n\t\treturn config.MCPServerConfig{}, fmt.Errorf(\"--header can only be used with http or sse transport\")\n\t}\n\n\tif looksLikeRemoteURL(target) {\n\t\treturn config.MCPServerConfig{}, fmt.Errorf(\n\t\t\t\"target %q looks like a remote MCP URL, but transport is %q. Use --transport http or --transport sse\",\n\t\t\ttarget,\n\t\t\ttransport,\n\t\t)\n\t}\n","sourceCodeStart":196,"sourceCodeEnd":232,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/cmd/picoclaw/internal/mcp/add.go#L196-L232","documentation":"Thrown by `picoclaw mcp add` when transport is http or sse and the target does not parse as an absolute URL. buildServerConfig runs url.ParseRequestURI(target) and additionally requires a non-empty Scheme and Host (add.go:212-215); anything missing either part is rejected with the raw target quoted. It is pure input validation before any config is written.","triggerScenarios":"`picoclaw mcp add mysvc --transport http localhost:8080/mcp` (no scheme), `--transport sse example.com/sse` (no scheme), a bare path like `/mcp` (no host), or a typo that makes ParseRequestURI fail.","commonSituations":"Copy-pasting an endpoint from vendor docs that list it as host:port without https://; shell quoting eating part of the URL; assuming the CLI infers the scheme like some other MCP clients do.","solutions":["Rewrite the target as a full absolute URL including scheme and host, e.g. https://example.com/mcp","Quote the whole URL so the shell cannot mangle it: picoclaw mcp add mysvc --transport http \"https://example.com/mcp\"","If the target is actually a local command, drop --transport http/sse and use stdio syntax"],"exampleFix":"# before\npicoclaw mcp add cache --transport http localhost:6379/mcp\n# after\npicoclaw mcp add cache --transport http https://localhost:6379/mcp","handlingStrategy":"validation","validationCode":"func validMCPURL(target string) bool {\n\tu, err := url.ParseRequestURI(target)\n\treturn err == nil && u.Scheme != \"\" && u.Host != \"\"\n}\n\n// before shelling out:\nif !validMCPURL(target) {\n\treturn fmt.Errorf(\"target %q must be an absolute URL like https://host/mcp\", target)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always pass http/sse endpoints as scheme+host URLs (https://example.com/mcp)","Quote the entire URL argument in shell scripts","In wrappers, pre-check with the same ParseRequestURI + Scheme + Host rules the CLI uses"],"tags":["cli","mcp","url-validation","http","sse"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}