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
- Remove `authRequired` from the tool if you want client-side OAuth (keep useClientOAuth: true)
- Remove `useClientOAuth: true` if you want to keep the authRequired list
- 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
- Pick one auth model per tool: either authRequired services or useClientOAuth
- When migrating to client OAuth, delete the authRequired list in the same change
- Add a config-lint rule asserting the two keys never co-occur
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- %s is not a valid type of auth service
- tool %q config error: parameter %q (index %d) references '%q
- tool %q config error: parameter %q cannot copy value from it
- doc %d: unexpected non-string key in input: %v
- doc %d: invalid config format at key %q: %w
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/16ee611ccad5dcef.
Report an issue: GitHub.