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

  1. 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
  2. If a different host is needed, define a separate http tool source with that BaseURL instead of overriding the path
  3. 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

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


AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05). Data as JSON: /api/errors/8225012523708a6c. Report an issue: GitHub.