googleapis/mcp-toolbox · error

`authRequired` and `useClientOAuth` are mutually exclusive.

Error message

`authRequired` and `useClientOAuth` are mutually exclusive. Choose only one authentication method

What it means

A tool config specified both `authRequired` and `useClientOAuth: true`. These are two different authorization models — server-enforced authorized services vs. delegating auth to the MCP client — and are mutually exclusive, so parsing is rejected.

Source

Thrown at internal/server/config.go:467

	actual := gemini.Config{Name: name}
	if err := dec.DecodeContext(ctx, &actual); err != nil {
		return nil, fmt.Errorf("unable to parse as %q: %w", name, err)
	}
	return actual, nil
}

func UnmarshalYAMLToolConfig(ctx context.Context, name string, r map[string]any) (tools.ToolConfig, error) {
	err := NameValidation(name)
	if err != nil {
		return nil, err
	}
	resourceType, ok := r["type"].(string)
	if !ok {
		return nil, fmt.Errorf("missing 'type' field or it is not a string")
	}
	// `authRequired` and `useClientOAuth` cannot be specified together
	if r["authRequired"] != nil && r["useClientOAuth"] == true {
		return nil, fmt.Errorf("`authRequired` and `useClientOAuth` are mutually exclusive. Choose only one authentication method")
	}
	// Make `authRequired` an empty list instead of nil for Tool manifest
	if r["authRequired"] == nil {
		r["authRequired"] = []string{}
	}

	// Parse scopesRequired if present
	if rawScopes, ok := r["scopesRequired"]; ok {
		if scopesList, ok := rawScopes.([]any); ok {
			var scopes []string
			for _, s := range scopesList {
				if str, ok := s.(string); ok {
					scopes = append(scopes, str)
				}
			}
			r["scopesRequired"] = scopes
		} else {
			return nil, fmt.Errorf("scopesRequired must be a list of strings")

View on GitHub (pinned to 8cc6e09de2)

Solutions

  1. Remove `authRequired` from the tool if you want client-side OAuth (keep useClientOAuth: true)
  2. Remove `useClientOAuth: true` if you want to keep the authRequired list
  3. Ensure only one authentication mechanism is configured per tool

Example fix

// before
execute-sql:
  type: postgres-sql
  authRequired:
    - my-google
  useClientOAuth: true
// after
execute-sql:
  type: postgres-sql
  useClientOAuth: true
Defensive patterns

Strategy: validation

Validate before calling

func validateToolAuth(cfg map[string]any) error {
  _, hasAuthReq := cfg["authRequired"]
  uco, hasUco := cfg["useClientOAuth"].(bool)
  if hasAuthReq && cfg["authRequired"] != nil && hasUco && uco {
    return fmt.Errorf("authRequired and useClientOAuth are mutually exclusive")
  }
  return nil
}

Prevention

When it happens

Trigger: A tools entry contains a non-null `authRequired` value AND `useClientOAuth: true` simultaneously, e.g. after adding useClientOAuth to an existing tool that already had authRequired: [my-auth].

Common situations: Migrating a tool from service-level auth to client OAuth but leaving the old authRequired list in place; copy-pasting auth blocks between tool definitions; misunderstanding that an empty-but-present authRequired still counts as specified.

Understand the failure class

Related errors


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