googleapis/mcp-toolbox · error
path must be relative and cannot override base host
Error message
path must be relative and cannot override base host
What it means
The HTTP tool resolves a user-supplied (templated) path against a fixed BaseURL. This library throws this error when the path is not purely relative — i.e. it contains a scheme (http://), a host, or userinfo — because letting the path override the base host would allow requests to arbitrary servers, breaking the tool's configured destination and enabling SSRF.
Source
Thrown at internal/tools/http/http.go:203
if err != nil {
return "", fmt.Errorf("error replacing pathParams: %s", err)
}
baseParsedURL, err := url.Parse(baseURL)
if err != nil {
return "", fmt.Errorf("error parsing base URL: %s", err)
}
if baseParsedURL.Scheme == "" || baseParsedURL.Host == "" {
return "", fmt.Errorf("base URL must include scheme and host")
}
relativePath := templatedPath.String()
relParsedURL, err := url.Parse(relativePath)
if err != nil {
return "", fmt.Errorf("error parsing URL path: %s", err)
}
if relParsedURL.Scheme != "" || relParsedURL.Host != "" || relParsedURL.User != nil {
return "", fmt.Errorf("path must be relative and cannot override base host")
}
// Reject dot segments before resolution
for _, segment := range strings.Split(relParsedURL.Path, "/") {
if segment == ".." {
return "", fmt.Errorf("path cannot contain dot segments (..)")
}
}
// Create URL based on BaseURL and Path
// Attach query parameters
parsedURL := baseParsedURL.ResolveReference(relParsedURL)
// Verify final path stays within base path scope
basePath := baseParsedURL.Path
finalPath := parsedURL.Path
if basePath != "/" {
requiredPrefix := strings.TrimSuffix(basePath, "/") + "/"View on GitHub (pinned to 8cc6e09de2)
Solutions
- Pass only the relative path portion (e.g. '/v1/items?id=1') in the path parameter and keep the host in the tool's BaseURL configuration
- If a different host is needed, define a separate http tool source with that BaseURL instead of overriding the path
- Check the templated values being substituted into the path (custom functions/templates may inject a full URL); strip scheme+host before substitution
Example fix
// before path: https://api.example.com/v1/items // after path: /v1/items
Defensive patterns
Strategy: validation
Validate before calling
u, err := url.Parse(pathParam)
if err != nil || u.Scheme != "" || u.Host != "" || u.User != nil {
return fmt.Errorf("path must be relative: %q", pathParam)
} Type guard
func isRelativePath(p string) bool {
u, err := url.Parse(p)
return err == nil && u.Scheme == "" && u.Host == "" && u.User == nil
} Prevention
- Always keep the host in the tool's BaseURL config, never in the path parameter
- Sanitize model/client-supplied URLs by extracting only the path portion before substitution
- Add a pre-invocation check that url.Parse of the path yields empty Scheme and Host
When it happens
Trigger: getURL is called (via Invoke) with a Path parameter that parses as an absolute URL, e.g. the caller passes 'https://evil.example.com/api' or '//evil.example.com/api' or 'user@host/path' as the templated path instead of a relative path like '/api/items'.
Common situations: Developers template the full URL into the path parameter because the model or client handed them a complete URL; config authors define an http tool whose base URL already includes a suffix path and then pass an absolute URL hoping to change hosts; SSRF-probe requests attempting host override.
Related errors
- URL scheme must be https, got %q
- URL host must be an allowed FHIR host, got %q
- path cannot contain dot segments (..)
- invalid allowedIpRanges: %w
- redirect to blocked IP %s denied
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/8225012523708a6c.
Report an issue: GitHub.